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

[FL-2051] BadUSB: new GUI (#844)

* [FL-2051] BadUsb: new GUI
* add missing assets
* skip empty lines and leading spaces
* SubGhz: add alignment check to keystore mess with iv routine

Co-authored-by: あく <alleteam@gmail.com>
Nikolay Minaylov 4 лет назад
Родитель
Сommit
f52e9fc578
43 измененных файлов с 1188 добавлено и 515 удалено
  1. 4 4
      applications/applications.c
  2. 85 0
      applications/bad_usb/bad_usb_app.c
  3. 11 0
      applications/bad_usb/bad_usb_app.h
  4. 35 0
      applications/bad_usb/bad_usb_app_i.h
  5. 463 0
      applications/bad_usb/bad_usb_script.c
  6. 45 0
      applications/bad_usb/bad_usb_script.h
  7. 30 0
      applications/bad_usb/scenes/bad_usb_scene.c
  8. 29 0
      applications/bad_usb/scenes/bad_usb_scene.h
  9. 2 0
      applications/bad_usb/scenes/bad_usb_scene_config.h
  10. 36 0
      applications/bad_usb/scenes/bad_usb_scene_file_select.c
  11. 47 0
      applications/bad_usb/scenes/bad_usb_scene_work.c
  12. 168 0
      applications/bad_usb/views/bad_usb_view.c
  13. 19 0
      applications/bad_usb/views/bad_usb_view.h
  14. 0 369
      applications/debug_tools/bad_usb.c
  15. 1 0
      applications/gpio/usb_uart_bridge.c
  16. 11 9
      applications/gpio/views/gpio_usb_uart.c
  17. 0 0
      assets/compiled/assets_icons.c
  18. 74 65
      assets/compiled/assets_icons.h
  19. BIN
      assets/icons/BadUsb/Clock_18x18.png
  20. BIN
      assets/icons/BadUsb/Error_18x18.png
  21. BIN
      assets/icons/BadUsb/EviSmile1_18x21.png
  22. BIN
      assets/icons/BadUsb/EviSmile2_18x21.png
  23. BIN
      assets/icons/BadUsb/EviWaiting1_18x21.png
  24. BIN
      assets/icons/BadUsb/EviWaiting2_18x21.png
  25. BIN
      assets/icons/BadUsb/Percent_10x14.png
  26. BIN
      assets/icons/BadUsb/Smile_18x18.png
  27. BIN
      assets/icons/BadUsb/UsbTree_48x22.png
  28. BIN
      assets/icons/MainMenu/BadUsb_14/frame_01.png
  29. BIN
      assets/icons/MainMenu/BadUsb_14/frame_02.png
  30. BIN
      assets/icons/MainMenu/BadUsb_14/frame_03.png
  31. BIN
      assets/icons/MainMenu/BadUsb_14/frame_04.png
  32. BIN
      assets/icons/MainMenu/BadUsb_14/frame_05.png
  33. BIN
      assets/icons/MainMenu/BadUsb_14/frame_06.png
  34. BIN
      assets/icons/MainMenu/BadUsb_14/frame_07.png
  35. BIN
      assets/icons/MainMenu/BadUsb_14/frame_08.png
  36. BIN
      assets/icons/MainMenu/BadUsb_14/frame_09.png
  37. BIN
      assets/icons/MainMenu/BadUsb_14/frame_10.png
  38. BIN
      assets/icons/MainMenu/BadUsb_14/frame_11.png
  39. 1 0
      assets/icons/MainMenu/BadUsb_14/frame_rate
  40. 58 34
      firmware/targets/f6/furi-hal/furi-hal-usb-hid.c
  41. 58 34
      firmware/targets/f7/furi-hal/furi-hal-usb-hid.c
  42. 9 0
      firmware/targets/furi-hal-include/furi-hal-usb-hid.h
  43. 2 0
      lib/subghz/subghz_keystore.c

+ 4 - 4
applications/applications.c

@@ -148,6 +148,10 @@ const FlipperApplication FLIPPER_APPS[] = {
     {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
 #endif
 
+#ifdef APP_BAD_USB
+    {.app = bad_usb_app, .name = "Bad USB", .stack_size = 2048, .icon = &A_BadUsb_14},
+#endif
+
 };
 
 const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplication);
@@ -238,10 +242,6 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
     {.app = usb_mouse_app, .name = "USB Mouse demo", .stack_size = 1024, .icon = NULL},
 #endif
 
-#ifdef APP_BAD_USB
-    {.app = bad_usb_app, .name = "Bad USB test", .stack_size = 2048, .icon = NULL},
-#endif
-
 #ifdef APP_UART_ECHO
     {.app = uart_echo_app, .name = "Uart Echo", .stack_size = 2048, .icon = NULL},
 #endif

+ 85 - 0
applications/bad_usb/bad_usb_app.c

@@ -0,0 +1,85 @@
+#include "bad_usb_app_i.h"
+#include <furi.h>
+#include <furi-hal.h>
+
+static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    BadUsbApp* app = context;
+    return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+static bool bad_usb_app_back_event_callback(void* context) {
+    furi_assert(context);
+    BadUsbApp* app = context;
+    return scene_manager_handle_back_event(app->scene_manager);
+}
+
+static void bad_usb_app_tick_event_callback(void* context) {
+    furi_assert(context);
+    BadUsbApp* app = context;
+    scene_manager_handle_tick_event(app->scene_manager);
+}
+
+BadUsbApp* bad_usb_app_alloc() {
+    BadUsbApp* app = furi_alloc(sizeof(BadUsbApp));
+
+    app->gui = furi_record_open("gui");
+    app->notifications = furi_record_open("notification");
+    app->dialogs = furi_record_open("dialogs");
+
+    app->view_dispatcher = view_dispatcher_alloc();
+    app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app);
+    view_dispatcher_enable_queue(app->view_dispatcher);
+    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+    view_dispatcher_set_tick_event_callback(
+        app->view_dispatcher, bad_usb_app_tick_event_callback, 500);
+
+    view_dispatcher_set_custom_event_callback(
+        app->view_dispatcher, bad_usb_app_custom_event_callback);
+    view_dispatcher_set_navigation_event_callback(
+        app->view_dispatcher, bad_usb_app_back_event_callback);
+
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+    app->bad_usb_view = bad_usb_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view));
+
+    scene_manager_next_scene(app->scene_manager, BadUsbAppViewFileSelect);
+
+    return app;
+}
+
+void bad_usb_app_free(BadUsbApp* app) {
+    furi_assert(app);
+
+    // Views
+    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewFileSelect);
+    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);
+    bad_usb_free(app->bad_usb_view);
+
+    // View dispatcher
+    view_dispatcher_free(app->view_dispatcher);
+    scene_manager_free(app->scene_manager);
+
+    // Close records
+    furi_record_close("gui");
+    furi_record_close("notification");
+    furi_record_close("dialogs");
+
+    free(app);
+}
+
+int32_t bad_usb_app(void* p) {
+    UsbInterface* usb_mode_prev = furi_hal_usb_get_config();
+    furi_hal_usb_set_config(&usb_hid);
+
+    BadUsbApp* bad_usb_app = bad_usb_app_alloc();
+
+    view_dispatcher_run(bad_usb_app->view_dispatcher);
+
+    furi_hal_usb_set_config(usb_mode_prev);
+    bad_usb_app_free(bad_usb_app);
+
+    return 0;
+}

+ 11 - 0
applications/bad_usb/bad_usb_app.h

@@ -0,0 +1,11 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BadUsbApp BadUsbApp;
+
+#ifdef __cplusplus
+}
+#endif

+ 35 - 0
applications/bad_usb/bad_usb_app_i.h

@@ -0,0 +1,35 @@
+#pragma once
+
+#include "bad_usb_app.h"
+#include "scenes/bad_usb_scene.h"
+#include "bad_usb_script.h"
+
+#include <gui/gui.h>
+#include <gui/view_dispatcher.h>
+#include <gui/scene_manager.h>
+#include <gui/modules/submenu.h>
+#include <dialogs/dialogs.h>
+#include <notification/notification-messages.h>
+#include <gui/modules/variable-item-list.h>
+#include "views/bad_usb_view.h"
+
+#define BAD_USB_APP_PATH_FOLDER "/any/badusb"
+#define BAD_USB_APP_EXTENSION ".txt"
+#define BAD_USB_FILE_NAME_LEN 40
+
+struct BadUsbApp {
+    Gui* gui;
+    ViewDispatcher* view_dispatcher;
+    SceneManager* scene_manager;
+    NotificationApp* notifications;
+    DialogsApp* dialogs;
+
+    char file_name[BAD_USB_FILE_NAME_LEN + 1];
+    BadUsb* bad_usb_view;
+    BadUsbScript* bad_usb_script;
+};
+
+typedef enum {
+    BadUsbAppViewFileSelect,
+    BadUsbAppViewWork,
+} BadUsbAppView;

+ 463 - 0
applications/bad_usb/bad_usb_script.c

@@ -0,0 +1,463 @@
+#include <furi.h>
+#include <furi-hal.h>
+#include <gui/gui.h>
+#include <input/input.h>
+#include <lib/toolbox/args.h>
+#include <furi-hal-usb-hid.h>
+#include <storage/storage.h>
+#include "bad_usb_script.h"
+
+#define TAG "BadUSB"
+#define WORKER_TAG TAG "Worker"
+#define FILE_BUFFER_LEN 16
+
+typedef enum {
+    WorkerEvtReserved = (1 << 0),
+    WorkerEvtToggle = (1 << 1),
+    WorkerEvtEnd = (1 << 2),
+    WorkerEvtConnect = (1 << 3),
+    WorkerEvtDisconnect = (1 << 4),
+} WorkerEvtFlags;
+
+struct BadUsbScript {
+    BadUsbState st;
+    string_t file_path;
+    uint32_t defdelay;
+    FuriThread* thread;
+    uint8_t file_buf[FILE_BUFFER_LEN + 1];
+    uint8_t buf_start;
+    uint8_t buf_len;
+    bool file_end;
+    string_t line;
+
+    string_t line_prev;
+    uint32_t repeat_cnt;
+};
+
+typedef struct {
+    char* name;
+    uint16_t keycode;
+} DuckyKey;
+
+static const DuckyKey ducky_keys[] = {
+    {"CTRL", KEY_MOD_LEFT_CTRL},
+    {"CONTROL", KEY_MOD_LEFT_CTRL},
+    {"SHIFT", KEY_MOD_LEFT_SHIFT},
+    {"ALT", KEY_MOD_LEFT_ALT},
+    {"GUI", KEY_MOD_LEFT_GUI},
+    {"WINDOWS", KEY_MOD_LEFT_GUI},
+
+    {"DOWNARROW", KEY_DOWN_ARROW},
+    {"DOWN", KEY_DOWN_ARROW},
+    {"LEFTARROW", KEY_LEFT_ARROW},
+    {"LEFT", KEY_LEFT_ARROW},
+    {"RIGHTARROW", KEY_RIGHT_ARROW},
+    {"RIGHT", KEY_RIGHT_ARROW},
+    {"UPARROW", KEY_UP_ARROW},
+    {"UP", KEY_UP_ARROW},
+
+    {"ENTER", KEY_ENTER},
+    {"BREAK", KEY_PAUSE},
+    {"PAUSE", KEY_PAUSE},
+    {"CAPSLOCK", KEY_CAPS_LOCK},
+    {"DELETE", KEY_DELETE},
+    {"BACKSPACE", KEY_BACKSPACE},
+    {"END", KEY_END},
+    {"ESC", KEY_ESC},
+    {"ESCAPE", KEY_ESC},
+    {"HOME", KEY_HOME},
+    {"INSERT", KEY_INSERT},
+    {"NUMLOCK", KEY_NUM_LOCK},
+    {"PAGEUP", KEY_PAGE_UP},
+    {"PAGEDOWN", KEY_PAGE_DOWN},
+    {"PRINTSCREEN", KEY_PRINT},
+    {"SCROLLOCK", KEY_SCROLL_LOCK},
+    {"SPACE", KEY_SPACE},
+    {"TAB", KEY_TAB},
+    {"MENU", KEY_APPLICATION},
+    {"APP", KEY_APPLICATION},
+
+    {"F1", KEY_F1},
+    {"F2", KEY_F2},
+    {"F3", KEY_F3},
+    {"F4", KEY_F4},
+    {"F5", KEY_F5},
+    {"F6", KEY_F6},
+    {"F7", KEY_F7},
+    {"F8", KEY_F8},
+    {"F9", KEY_F9},
+    {"F10", KEY_F10},
+    {"F11", KEY_F11},
+    {"F12", KEY_F12},
+};
+
+static const char ducky_cmd_comment[] = {"REM"};
+static const char ducky_cmd_delay[] = {"DELAY"};
+static const char ducky_cmd_string[] = {"STRING"};
+static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY"};
+static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY"};
+static const char ducky_cmd_repeat[] = {"REPEAT"};
+
+static bool ducky_get_number(char* param, uint32_t* val) {
+    uint32_t value = 0;
+    if(sscanf(param, "%lu", &value) == 1) {
+        *val = value;
+        return true;
+    }
+    return false;
+}
+
+static uint32_t ducky_get_command_len(char* line) {
+    uint32_t len = strlen(line);
+    for(uint32_t i = 0; i < len; i++) {
+        if(line[i] == ' ') return i;
+    }
+    return 0;
+}
+
+static bool ducky_string(char* param) {
+    uint32_t i = 0;
+    while(param[i] != '\0') {
+        furi_hal_hid_kb_press(HID_ASCII_TO_KEY(param[i]));
+        furi_hal_hid_kb_release(HID_ASCII_TO_KEY(param[i]));
+        i++;
+    }
+    return true;
+}
+
+static uint16_t ducky_get_keycode(char* param, bool accept_chars) {
+    for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
+        if(strncmp(param, ducky_keys[i].name, strlen(ducky_keys[i].name)) == 0)
+            return ducky_keys[i].keycode;
+    }
+    if((accept_chars) && (strlen(param) > 0)) {
+        return (HID_ASCII_TO_KEY(param[0]) & 0xFF);
+    }
+    return 0;
+}
+
+static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) {
+    uint32_t line_len = string_size(line);
+    char* line_t = (char*)string_get_cstr(line);
+    bool state = false;
+
+    for(uint32_t i = 0; i < line_len; i++) {
+        if((line_t[i] != ' ') && (line_t[i] != '\t') && (line_t[i] != '\n')) {
+            line_t = &line_t[i];
+            break; // Skip spaces and tabs
+        }
+        if(i == line_len - 1) return 0; // Skip empty lines
+    }
+
+    FURI_LOG_I(WORKER_TAG, "line:%s", line_t);
+
+    // General commands
+    if(strncmp(line_t, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
+        // REM - comment line
+        return (0);
+    } else if(strncmp(line_t, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
+        // DELAY
+        line_t = &line_t[ducky_get_command_len(line_t) + 1];
+        uint32_t delay_val = 0;
+        state = ducky_get_number(line_t, &delay_val);
+        if((state) && (delay_val > 0)) {
+            return (int32_t)delay_val;
+        }
+        return (-1);
+    } else if(
+        (strncmp(line_t, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
+        (strncmp(line_t, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) {
+        // DEFAULT_DELAY
+        line_t = &line_t[ducky_get_command_len(line_t) + 1];
+        state = ducky_get_number(line_t, &bad_usb->defdelay);
+        return (state) ? (0) : (-1);
+    } else if(strncmp(line_t, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
+        // STRING
+        line_t = &line_t[ducky_get_command_len(line_t) + 1];
+        state = ducky_string(line_t);
+        return (state) ? (0) : (-1);
+    } else if(strncmp(line_t, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
+        // REPEAT
+        line_t = &line_t[ducky_get_command_len(line_t) + 1];
+        state = ducky_get_number(line_t, &bad_usb->repeat_cnt);
+        return (state) ? (0) : (-1);
+    } else {
+        // Special keys + modifiers
+        uint16_t key = ducky_get_keycode(line_t, false);
+        if(key == KEY_NONE) return (-1);
+        if((key & 0xFF00) != 0) {
+            // It's a modifier key
+            line_t = &line_t[ducky_get_command_len(line_t) + 1];
+            key |= ducky_get_keycode(line_t, true);
+        }
+        furi_hal_hid_kb_press(key);
+        furi_hal_hid_kb_release(key);
+        return (0);
+    }
+    return (-1);
+}
+
+static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) {
+    uint8_t ret = 0;
+    uint32_t line_len = 0;
+
+    do {
+        ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN);
+        for(uint16_t i = 0; i < ret; i++) {
+            if(bad_usb->file_buf[i] == '\n' && line_len > 0) {
+                bad_usb->st.line_nb++;
+                line_len = 0;
+            } else {
+                line_len++;
+            }
+        }
+        if(storage_file_eof(script_file)) {
+            if(line_len > 0) {
+                bad_usb->st.line_nb++;
+                break;
+            }
+        }
+    } while(ret > 0);
+
+    storage_file_seek(script_file, 0, true);
+
+    return true;
+}
+
+static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file) {
+    int32_t delay_val = 0;
+
+    if(bad_usb->repeat_cnt > 0) {
+        bad_usb->repeat_cnt--;
+        delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev);
+        if(delay_val < 0) {
+            bad_usb->st.error_line = bad_usb->st.line_cur - 1;
+            FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur - 1);
+            return (-1);
+        } else {
+            return (delay_val + bad_usb->defdelay);
+        }
+    }
+
+    string_set(bad_usb->line_prev, bad_usb->line);
+    string_reset(bad_usb->line);
+
+    while(1) {
+        if(bad_usb->buf_len == 0) {
+            bad_usb->buf_len = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN);
+            if(storage_file_eof(script_file)) {
+                if((bad_usb->buf_len < FILE_BUFFER_LEN) && (bad_usb->file_end == false)) {
+                    bad_usb->file_buf[bad_usb->buf_len] = '\n';
+                    bad_usb->buf_len++;
+                    bad_usb->file_end = true;
+                }
+            }
+
+            bad_usb->buf_start = 0;
+            if(bad_usb->buf_len == 0) return (-2);
+        }
+        for(uint8_t i = bad_usb->buf_start; i < (bad_usb->buf_start + bad_usb->buf_len); i++) {
+            if(bad_usb->file_buf[i] == '\n' && string_size(bad_usb->line) > 0) {
+                bad_usb->st.line_cur++;
+                bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
+                bad_usb->buf_start = i + 1;
+                delay_val = ducky_parse_line(bad_usb, bad_usb->line);
+                if(delay_val < 0) {
+                    bad_usb->st.error_line = bad_usb->st.line_cur;
+                    FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur);
+                    return (-1);
+                } else {
+                    return (delay_val + bad_usb->defdelay);
+                }
+            } else {
+                string_push_back(bad_usb->line, bad_usb->file_buf[i]);
+            }
+        }
+        bad_usb->buf_len = 0;
+        if(bad_usb->file_end) return (-2);
+    }
+
+    return 0;
+}
+
+static void bad_usb_hid_state_callback(bool state, void* context) {
+    furi_assert(context);
+    BadUsbScript* bad_usb = context;
+
+    if(state == true)
+        osThreadFlagsSet(furi_thread_get_thread_id(bad_usb->thread), WorkerEvtConnect);
+    else
+        osThreadFlagsSet(furi_thread_get_thread_id(bad_usb->thread), WorkerEvtDisconnect);
+}
+
+static int32_t bad_usb_worker(void* context) {
+    BadUsbScript* bad_usb = context;
+
+    BadUsbWorkerState worker_state = BadUsbStateInit;
+    int32_t delay_val = 0;
+
+    FURI_LOG_I(WORKER_TAG, "Init");
+    File* script_file = storage_file_alloc(furi_record_open("storage"));
+    string_init(bad_usb->line);
+    string_init(bad_usb->line_prev);
+
+    furi_hal_hid_set_state_callback(bad_usb_hid_state_callback, bad_usb);
+
+    while(1) {
+        if(worker_state == BadUsbStateInit) { // State: initialization
+            if(storage_file_open(
+                   script_file,
+                   string_get_cstr(bad_usb->file_path),
+                   FSAM_READ,
+                   FSOM_OPEN_EXISTING)) {
+                if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) {
+                    if(furi_hal_hid_is_connected()) {
+                        worker_state = BadUsbStateIdle; // Ready to run
+                    } else {
+                        worker_state = BadUsbStateNotConnected; // USB not connected
+                    }
+                } else {
+                    worker_state = BadUsbStateScriptError; // Script preload error
+                }
+            } else {
+                FURI_LOG_E(WORKER_TAG, "File open error");
+                worker_state = BadUsbStateFileError; // File open error
+            }
+            bad_usb->st.state = worker_state;
+
+        } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
+            uint32_t flags =
+                osThreadFlagsWait(WorkerEvtEnd | WorkerEvtConnect, osFlagsWaitAny, osWaitForever);
+            furi_check((flags & osFlagsError) == 0);
+            if(flags & WorkerEvtEnd) {
+                break;
+            } else if(flags & WorkerEvtConnect) {
+                worker_state = BadUsbStateIdle; // Ready to run
+            }
+            bad_usb->st.state = worker_state;
+
+        } else if(worker_state == BadUsbStateIdle) { // State: ready to start
+            uint32_t flags = osThreadFlagsWait(
+                WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
+                osFlagsWaitAny,
+                osWaitForever);
+            furi_check((flags & osFlagsError) == 0);
+            if(flags & WorkerEvtEnd) {
+                break;
+            } else if(flags & WorkerEvtToggle) { // Start executing script
+                delay_val = 0;
+                bad_usb->buf_len = 0;
+                bad_usb->st.line_cur = 0;
+                bad_usb->defdelay = 0;
+                bad_usb->repeat_cnt = 0;
+                bad_usb->file_end = false;
+                storage_file_seek(script_file, 0, true);
+                worker_state = BadUsbStateRunning;
+            } else if(flags & WorkerEvtDisconnect) {
+                worker_state = BadUsbStateNotConnected; // USB disconnected
+            }
+            bad_usb->st.state = worker_state;
+
+        } else if(worker_state == BadUsbStateRunning) { // State: running
+            uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
+            uint32_t flags = osThreadFlagsWait(
+                WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, osFlagsWaitAny, delay_cur);
+            delay_val -= delay_cur;
+            if(!(flags & osFlagsError)) {
+                if(flags & WorkerEvtEnd) {
+                    break;
+                } else if(flags & WorkerEvtToggle) {
+                    worker_state = BadUsbStateIdle; // Stop executing script
+                    furi_hal_hid_kb_release_all();
+                } else if(flags & WorkerEvtDisconnect) {
+                    worker_state = BadUsbStateNotConnected; // USB disconnected
+                    furi_hal_hid_kb_release_all();
+                }
+                bad_usb->st.state = worker_state;
+                continue;
+            } else if((flags == osFlagsErrorTimeout) || (flags == osFlagsErrorResource)) {
+                if(delay_val > 0) {
+                    bad_usb->st.delay_remain--;
+                    continue;
+                }
+                bad_usb->st.state = BadUsbStateRunning;
+                delay_val = ducky_script_execute_next(bad_usb, script_file);
+                if(delay_val == -1) { // Script error
+                    delay_val = 0;
+                    worker_state = BadUsbStateScriptError;
+                    bad_usb->st.state = worker_state;
+                } else if(delay_val == -2) { // End of script
+                    delay_val = 0;
+                    worker_state = BadUsbStateIdle;
+                    bad_usb->st.state = BadUsbStateDone;
+                    furi_hal_hid_kb_release_all();
+                    continue;
+                } else if(delay_val > 1000) {
+                    bad_usb->st.state = BadUsbStateDelay; // Show long delays
+                    bad_usb->st.delay_remain = delay_val / 1000;
+                }
+            } else {
+                furi_check((flags & osFlagsError) == 0);
+            }
+
+        } else if(
+            (worker_state == BadUsbStateFileError) ||
+            (worker_state == BadUsbStateScriptError)) { // State: error
+            uint32_t flags = osThreadFlagsWait(
+                WorkerEvtEnd, osFlagsWaitAny, osWaitForever); // Waiting for exit command
+            furi_check((flags & osFlagsError) == 0);
+            if(flags & WorkerEvtEnd) {
+                break;
+            }
+        }
+    }
+
+    furi_hal_hid_set_state_callback(NULL, NULL);
+
+    storage_file_close(script_file);
+    storage_file_free(script_file);
+    string_clear(bad_usb->line);
+    string_clear(bad_usb->line_prev);
+
+    FURI_LOG_I(WORKER_TAG, "End");
+
+    return 0;
+}
+
+BadUsbScript* bad_usb_script_open(string_t file_path) {
+    furi_assert(file_path);
+
+    BadUsbScript* bad_usb = furi_alloc(sizeof(BadUsbScript));
+    string_init(bad_usb->file_path);
+    string_set(bad_usb->file_path, file_path);
+
+    bad_usb->st.state = BadUsbStateInit;
+
+    bad_usb->thread = furi_thread_alloc();
+    furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
+    furi_thread_set_stack_size(bad_usb->thread, 2048);
+    furi_thread_set_context(bad_usb->thread, bad_usb);
+    furi_thread_set_callback(bad_usb->thread, bad_usb_worker);
+
+    furi_thread_start(bad_usb->thread);
+    return bad_usb;
+}
+
+void bad_usb_script_close(BadUsbScript* bad_usb) {
+    furi_assert(bad_usb);
+    osThreadFlagsSet(furi_thread_get_thread_id(bad_usb->thread), WorkerEvtEnd);
+    furi_thread_join(bad_usb->thread);
+    furi_thread_free(bad_usb->thread);
+    string_clear(bad_usb->file_path);
+    free(bad_usb);
+}
+
+void bad_usb_script_toggle(BadUsbScript* bad_usb) {
+    furi_assert(bad_usb);
+    osThreadFlagsSet(furi_thread_get_thread_id(bad_usb->thread), WorkerEvtToggle);
+}
+
+BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) {
+    furi_assert(bad_usb);
+    return &(bad_usb->st);
+}

+ 45 - 0
applications/bad_usb/bad_usb_script.h

@@ -0,0 +1,45 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <furi.h>
+#include <m-string.h>
+
+typedef struct BadUsbScript BadUsbScript;
+
+typedef enum {
+    BadUsbStateInit,
+    BadUsbStateNotConnected,
+    BadUsbStateIdle,
+    BadUsbStateRunning,
+    BadUsbStateDelay,
+    BadUsbStateDone,
+    BadUsbStateScriptError,
+    BadUsbStateFileError,
+} BadUsbWorkerState;
+
+typedef struct {
+    BadUsbWorkerState state;
+    uint16_t line_cur;
+    uint16_t line_nb;
+    uint32_t delay_remain;
+    uint16_t error_line;
+} BadUsbState;
+
+BadUsbScript* bad_usb_script_open(string_t file_path);
+
+void bad_usb_script_close(BadUsbScript* bad_usb);
+
+void bad_usb_script_start(BadUsbScript* bad_usb);
+
+void bad_usb_script_stop(BadUsbScript* bad_usb);
+
+void bad_usb_script_toggle(BadUsbScript* bad_usb);
+
+BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb);
+
+#ifdef __cplusplus
+}
+#endif

+ 30 - 0
applications/bad_usb/scenes/bad_usb_scene.c

@@ -0,0 +1,30 @@
+#include "bad_usb_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const bad_usb_scene_on_enter_handlers[])(void*) = {
+#include "bad_usb_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const bad_usb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "bad_usb_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const bad_usb_scene_on_exit_handlers[])(void* context) = {
+#include "bad_usb_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers bad_usb_scene_handlers = {
+    .on_enter_handlers = bad_usb_scene_on_enter_handlers,
+    .on_event_handlers = bad_usb_scene_on_event_handlers,
+    .on_exit_handlers = bad_usb_scene_on_exit_handlers,
+    .scene_num = BadUsbSceneNum,
+};

+ 29 - 0
applications/bad_usb/scenes/bad_usb_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) BadUsbScene##id,
+typedef enum {
+#include "bad_usb_scene_config.h"
+    BadUsbSceneNum,
+} BadUsbScene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers bad_usb_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "bad_usb_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_event handlers declaration
+#define ADD_SCENE(prefix, name, id) \
+    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
+#include "bad_usb_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
+#include "bad_usb_scene_config.h"
+#undef ADD_SCENE

+ 2 - 0
applications/bad_usb/scenes/bad_usb_scene_config.h

@@ -0,0 +1,2 @@
+ADD_SCENE(bad_usb, file_select, FileSelect)
+ADD_SCENE(bad_usb, work, Work)

+ 36 - 0
applications/bad_usb/scenes/bad_usb_scene_file_select.c

@@ -0,0 +1,36 @@
+#include "../bad_usb_app_i.h"
+#include "furi-hal-power.h"
+
+static bool bad_usb_file_select(BadUsbApp* bad_usb) {
+    furi_assert(bad_usb);
+
+    // Input events and views are managed by file_select
+    bool res = dialog_file_select_show(
+        bad_usb->dialogs,
+        BAD_USB_APP_PATH_FOLDER,
+        BAD_USB_APP_EXTENSION,
+        bad_usb->file_name,
+        sizeof(bad_usb->file_name),
+        NULL);
+    return res;
+}
+
+void bad_usb_scene_file_select_on_enter(void* context) {
+    BadUsbApp* bad_usb = context;
+
+    if(bad_usb_file_select(bad_usb)) {
+        scene_manager_next_scene(bad_usb->scene_manager, BadUsbAppViewWork);
+    } else {
+        //scene_manager_previous_scene(bad_usb->scene_manager);
+        view_dispatcher_stop(bad_usb->view_dispatcher);
+    }
+}
+
+bool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) {
+    // BadUsbApp* bad_usb = context;
+    return false;
+}
+
+void bad_usb_scene_file_select_on_exit(void* context) {
+    // BadUsbApp* bad_usb = context;
+}

+ 47 - 0
applications/bad_usb/scenes/bad_usb_scene_work.c

@@ -0,0 +1,47 @@
+#include "../bad_usb_script.h"
+#include "../bad_usb_app_i.h"
+#include "../views/bad_usb_view.h"
+#include "furi-hal.h"
+
+void bad_usb_scene_work_ok_callback(InputType type, void* context) {
+    furi_assert(context);
+    BadUsbApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, type);
+}
+
+bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
+    BadUsbApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        bad_usb_script_toggle(app->bad_usb_script);
+        consumed = true;
+    } else if(event.type == SceneManagerEventTypeTick) {
+        bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
+    }
+    return consumed;
+}
+
+void bad_usb_scene_work_on_enter(void* context) {
+    BadUsbApp* app = context;
+
+    string_t file_name;
+    string_init(file_name);
+
+    bad_usb_set_file_name(app->bad_usb_view, app->file_name);
+    string_printf(
+        file_name, "%s/%s%s", BAD_USB_APP_PATH_FOLDER, app->file_name, BAD_USB_APP_EXTENSION);
+    app->bad_usb_script = bad_usb_script_open(file_name);
+
+    string_clear(file_name);
+
+    bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
+
+    bad_usb_set_ok_callback(app->bad_usb_view, bad_usb_scene_work_ok_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork);
+}
+
+void bad_usb_scene_work_on_exit(void* context) {
+    BadUsbApp* app = context;
+    bad_usb_script_close(app->bad_usb_script);
+}

+ 168 - 0
applications/bad_usb/views/bad_usb_view.c

@@ -0,0 +1,168 @@
+#include "bad_usb_view.h"
+#include "../bad_usb_script.h"
+#include <gui/elements.h>
+
+struct BadUsb {
+    View* view;
+    BadUsbOkCallback callback;
+    void* context;
+};
+
+typedef struct {
+    char* file_name;
+    BadUsbState state;
+    uint8_t anim_frame;
+} BadUsbModel;
+
+static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
+    BadUsbModel* model = _model;
+
+    string_t disp_str;
+    string_init_set_str(disp_str, model->file_name);
+    elements_string_fit_width(canvas, disp_str, 128 - 2);
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 2, 8, string_get_cstr(disp_str));
+    string_reset(disp_str);
+
+    canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22);
+
+    if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) {
+        elements_button_center(canvas, "Run");
+    } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
+        elements_button_center(canvas, "Stop");
+    }
+
+    if(model->state.state == BadUsbStateNotConnected) {
+        canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18);
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect");
+        canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB");
+    } else if(model->state.state == BadUsbStateFileError) {
+        canvas_draw_icon(canvas, 4, 22, &I_Error_18x18);
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "File");
+        canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "ERROR");
+    } else if(model->state.state == BadUsbStateScriptError) {
+        canvas_draw_icon(canvas, 4, 22, &I_Error_18x18);
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
+        canvas_set_font(canvas, FontSecondary);
+        string_printf(disp_str, "line %u", model->state.error_line);
+        canvas_draw_str_aligned(
+            canvas, 127, 46, AlignRight, AlignBottom, string_get_cstr(disp_str));
+        string_reset(disp_str);
+    } else if(model->state.state == BadUsbStateIdle) {
+        canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18);
+        canvas_set_font(canvas, FontBigNumbers);
+        canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "0");
+        canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14);
+    } else if(model->state.state == BadUsbStateRunning) {
+        if(model->anim_frame == 0) {
+            canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21);
+        } else {
+            canvas_draw_icon(canvas, 4, 19, &I_EviSmile2_18x21);
+        }
+        canvas_set_font(canvas, FontBigNumbers);
+        string_printf(disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
+        canvas_draw_str_aligned(
+            canvas, 114, 36, AlignRight, AlignBottom, string_get_cstr(disp_str));
+        string_reset(disp_str);
+        canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14);
+    } else if(model->state.state == BadUsbStateDone) {
+        canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21);
+        canvas_set_font(canvas, FontBigNumbers);
+        canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "100");
+        string_reset(disp_str);
+        canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14);
+    } else if(model->state.state == BadUsbStateDelay) {
+        if(model->anim_frame == 0) {
+            canvas_draw_icon(canvas, 4, 19, &I_EviWaiting1_18x21);
+        } else {
+            canvas_draw_icon(canvas, 4, 19, &I_EviWaiting2_18x21);
+        }
+        canvas_set_font(canvas, FontBigNumbers);
+        string_printf(disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
+        canvas_draw_str_aligned(
+            canvas, 114, 36, AlignRight, AlignBottom, string_get_cstr(disp_str));
+        string_reset(disp_str);
+        canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14);
+        canvas_set_font(canvas, FontSecondary);
+        string_printf(disp_str, "delay %us", model->state.delay_remain);
+        canvas_draw_str_aligned(
+            canvas, 127, 46, AlignRight, AlignBottom, string_get_cstr(disp_str));
+        string_reset(disp_str);
+    } else {
+        canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18);
+    }
+
+    string_clear(disp_str);
+}
+
+static bool bad_usb_input_callback(InputEvent* event, void* context) {
+    furi_assert(context);
+    BadUsb* bad_usb = context;
+    bool consumed = false;
+
+    if(event->type == InputTypeShort) {
+        if(event->key == InputKeyOk) {
+            consumed = true;
+            furi_assert(bad_usb->callback);
+            bad_usb->callback(InputTypeShort, bad_usb->context);
+        }
+    }
+
+    return consumed;
+}
+
+BadUsb* bad_usb_alloc() {
+    BadUsb* bad_usb = furi_alloc(sizeof(BadUsb));
+
+    bad_usb->view = view_alloc();
+    view_allocate_model(bad_usb->view, ViewModelTypeLocking, sizeof(BadUsbModel));
+    view_set_context(bad_usb->view, bad_usb);
+    view_set_draw_callback(bad_usb->view, bad_usb_draw_callback);
+    view_set_input_callback(bad_usb->view, bad_usb_input_callback);
+
+    return bad_usb;
+}
+
+void bad_usb_free(BadUsb* bad_usb) {
+    furi_assert(bad_usb);
+    view_free(bad_usb->view);
+    free(bad_usb);
+}
+
+View* bad_usb_get_view(BadUsb* bad_usb) {
+    furi_assert(bad_usb);
+    return bad_usb->view;
+}
+
+void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context) {
+    furi_assert(bad_usb);
+    furi_assert(callback);
+    with_view_model(
+        bad_usb->view, (BadUsbModel * model) {
+            bad_usb->callback = callback;
+            bad_usb->context = context;
+            return false;
+        });
+}
+
+void bad_usb_set_file_name(BadUsb* bad_usb, char* name) {
+    furi_assert(name);
+    with_view_model(
+        bad_usb->view, (BadUsbModel * model) {
+            model->file_name = name;
+            return false;
+        });
+}
+
+void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) {
+    furi_assert(st);
+    with_view_model(
+        bad_usb->view, (BadUsbModel * model) {
+            memcpy(&(model->state), st, sizeof(BadUsbState));
+            model->anim_frame ^= 1;
+            return false;
+        });
+}

+ 19 - 0
applications/bad_usb/views/bad_usb_view.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../bad_usb_script.h"
+
+typedef struct BadUsb BadUsb;
+typedef void (*BadUsbOkCallback)(InputType type, void* context);
+
+BadUsb* bad_usb_alloc();
+
+void bad_usb_free(BadUsb* bad_usb);
+
+View* bad_usb_get_view(BadUsb* bad_usb);
+
+void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context);
+
+void bad_usb_set_file_name(BadUsb* bad_usb, char* name);
+
+void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st);

+ 0 - 369
applications/debug_tools/bad_usb.c

@@ -1,369 +0,0 @@
-#include <furi.h>
-#include <furi-hal.h>
-#include <gui/gui.h>
-#include <input/input.h>
-#include <lib/toolbox/args.h>
-#include <furi-hal-usb-hid.h>
-#include <storage/storage.h>
-
-#define TAG "BadUsb"
-#define WORKER_TAG TAG "Worker"
-
-typedef enum {
-    EventTypeInput,
-    EventTypeWorkerState,
-} EventType;
-
-typedef enum {
-    WorkerStateDone,
-    WorkerStateNoFile,
-    WorkerStateScriptError,
-    WorkerStateDisconnected,
-} WorkerState;
-
-typedef enum {
-    AppStateWait,
-    AppStateRunning,
-    AppStateError,
-    AppStateExit,
-} AppState;
-
-typedef enum {
-    WorkerCmdStart = (1 << 0),
-    WorkerCmdStop = (1 << 1),
-} WorkerCommandFlags;
-
-// Event message from worker
-typedef struct {
-    WorkerState state;
-    uint16_t line;
-} BadUsbWorkerState;
-
-typedef struct {
-    union {
-        InputEvent input;
-        BadUsbWorkerState worker;
-    };
-    EventType type;
-} BadUsbEvent;
-
-typedef struct {
-    uint32_t defdelay;
-    char msg_text[32];
-    osThreadAttr_t thread_attr;
-    osThreadId_t thread;
-    osMessageQueueId_t event_queue;
-} BadUsbParams;
-
-typedef struct {
-    char* name;
-    uint16_t keycode;
-} DuckyKey;
-
-static const DuckyKey ducky_keys[] = {
-    {"CTRL", KEY_MOD_LEFT_CTRL},
-    {"CONTROL", KEY_MOD_LEFT_CTRL},
-    {"SHIFT", KEY_MOD_LEFT_SHIFT},
-    {"ALT", KEY_MOD_LEFT_ALT},
-    {"GUI", KEY_MOD_LEFT_GUI},
-    {"WINDOWS", KEY_MOD_LEFT_GUI},
-
-    {"DOWNARROW", KEY_DOWN_ARROW},
-    {"DOWN", KEY_DOWN_ARROW},
-    {"LEFTARROW", KEY_LEFT_ARROW},
-    {"LEFT", KEY_LEFT_ARROW},
-    {"RIGHTARROW", KEY_RIGHT_ARROW},
-    {"RIGHT", KEY_RIGHT_ARROW},
-    {"UPARROW", KEY_UP_ARROW},
-    {"UP", KEY_UP_ARROW},
-
-    {"ENTER", KEY_ENTER},
-    {"BREAK", KEY_PAUSE},
-    {"PAUSE", KEY_PAUSE},
-    {"CAPSLOCK", KEY_CAPS_LOCK},
-    {"DELETE", KEY_DELETE},
-    {"BACKSPACE", KEY_BACKSPACE},
-    {"END", KEY_END},
-    {"ESC", KEY_ESC},
-    {"ESCAPE", KEY_ESC},
-    {"HOME", KEY_HOME},
-    {"INSERT", KEY_INSERT},
-    {"NUMLOCK", KEY_NUM_LOCK},
-    {"PAGEUP", KEY_PAGE_UP},
-    {"PAGEDOWN", KEY_PAGE_DOWN},
-    {"PRINTSCREEN", KEY_PRINT},
-    {"SCROLLOCK", KEY_SCROLL_LOCK},
-    {"SPACE", KEY_SPACE},
-    {"TAB", KEY_TAB},
-    {"MENU", KEY_APPLICATION},
-    {"APP", KEY_APPLICATION},
-};
-
-static const char ducky_cmd_comment[] = {"REM"};
-static const char ducky_cmd_delay[] = {"DELAY"};
-static const char ducky_cmd_string[] = {"STRING"};
-static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY"};
-static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY"};
-
-static bool ducky_get_delay_val(char* param, uint32_t* val) {
-    uint32_t delay_val = 0;
-    if(sscanf(param, "%lu", &delay_val) == 1) {
-        *val = delay_val;
-        return true;
-    }
-    return false;
-}
-
-static bool ducky_string(char* param) {
-    uint32_t i = 0;
-    while(param[i] != '\0') {
-        furi_hal_hid_kb_press(HID_ASCII_TO_KEY(param[i]));
-        furi_hal_hid_kb_release(HID_ASCII_TO_KEY(param[i]));
-        i++;
-    }
-    return true;
-}
-
-static uint16_t ducky_get_keycode(char* param, bool accept_chars) {
-    for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
-        if(strncmp(param, ducky_keys[i].name, strlen(ducky_keys[i].name)) == 0)
-            return ducky_keys[i].keycode;
-    }
-    if((accept_chars) && (strlen(param) > 0)) {
-        return (HID_ASCII_TO_KEY(param[0]) & 0xFF);
-    }
-    return 0;
-}
-
-static bool ducky_parse_line(string_t line, BadUsbParams* app) {
-    //uint32_t line_len = string_size(line);
-    char* line_t = (char*)string_get_cstr(line);
-    bool state = false;
-
-    // General commands
-    if(strncmp(line_t, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
-        // REM - comment line
-        return true;
-    } else if(strncmp(line_t, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
-        // DELAY
-        line_t = &line_t[args_get_first_word_length(line) + 1];
-        uint32_t delay_val = 0;
-        state = ducky_get_delay_val(line_t, &delay_val);
-        if((state) && (delay_val > 0)) {
-            // Using ThreadFlagsWait as delay function allows exiting task on WorkerCmdStop command
-            if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, delay_val) ==
-               WorkerCmdStop)
-                return true;
-        }
-        return state;
-    } else if(
-        (strncmp(line_t, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
-        (strncmp(line_t, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) {
-        // DEFAULT_DELAY
-        line_t = &line_t[args_get_first_word_length(line) + 1];
-        return ducky_get_delay_val(line_t, &app->defdelay);
-    } else if(strncmp(line_t, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
-        // STRING
-        if(app->defdelay > 0) {
-            if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, app->defdelay) ==
-               WorkerCmdStop)
-                return true;
-        }
-        line_t = &line_t[args_get_first_word_length(line) + 1];
-        return ducky_string(line_t);
-    } else {
-        // Special keys + modifiers
-        uint16_t key = ducky_get_keycode(line_t, false);
-        if(key == KEY_NONE) return false;
-        if((key & 0xFF00) != 0) {
-            // It's a modifier key
-            line_t = &line_t[args_get_first_word_length(line) + 1];
-            key |= ducky_get_keycode(line_t, true);
-        }
-        if(app->defdelay > 0) {
-            if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, app->defdelay) ==
-               WorkerCmdStop)
-                return true;
-        }
-        furi_hal_hid_kb_press(key);
-        furi_hal_hid_kb_release(key);
-        return true;
-    }
-    return false;
-}
-
-static void badusb_worker(void* context) {
-    BadUsbParams* app = context;
-    FURI_LOG_I(WORKER_TAG, "Init");
-    File* script_file = storage_file_alloc(furi_record_open("storage"));
-    BadUsbEvent evt;
-    string_t line;
-    uint32_t line_cnt = 0;
-    string_init(line);
-    if(storage_file_open(script_file, "/ext/badusb.txt", FSAM_READ, FSOM_OPEN_EXISTING)) {
-        char buffer[16];
-        uint16_t ret;
-        uint32_t flags =
-            osThreadFlagsWait(WorkerCmdStart | WorkerCmdStop, osFlagsWaitAny, osWaitForever);
-        if(flags & WorkerCmdStart) {
-            FURI_LOG_I(WORKER_TAG, "Start");
-            do {
-                ret = storage_file_read(script_file, buffer, 16);
-                for(uint16_t i = 0; i < ret; i++) {
-                    if(buffer[i] == '\n' && string_size(line) > 0) {
-                        line_cnt++;
-                        if(ducky_parse_line(line, app) == false) {
-                            ret = 0;
-                            FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", line_cnt);
-                            evt.type = EventTypeWorkerState;
-                            evt.worker.state = WorkerStateScriptError;
-                            evt.worker.line = line_cnt;
-                            osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
-                            break;
-                        }
-                        flags = osThreadFlagsGet();
-                        if(flags == WorkerCmdStop) {
-                            ret = 0;
-                            break;
-                        }
-                        string_reset(line);
-                    } else {
-                        string_push_back(line, buffer[i]);
-                    }
-                }
-            } while(ret > 0);
-        }
-    } else {
-        FURI_LOG_E(WORKER_TAG, "Script file open error");
-        evt.type = EventTypeWorkerState;
-        evt.worker.state = WorkerStateNoFile;
-        osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
-    }
-    string_reset(line);
-    string_clear(line);
-
-    furi_hal_hid_kb_release_all();
-    storage_file_close(script_file);
-    storage_file_free(script_file);
-
-    FURI_LOG_I(WORKER_TAG, "End");
-    evt.type = EventTypeWorkerState;
-    evt.worker.state = WorkerStateDone;
-    osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
-
-    furi_hal_hid_kb_release_all();
-
-    osThreadExit();
-}
-
-static void bad_usb_render_callback(Canvas* canvas, void* ctx) {
-    BadUsbParams* app = (BadUsbParams*)ctx;
-
-    canvas_clear(canvas);
-
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 0, 10, "Bad USB test");
-
-    if(strlen(app->msg_text) > 0) {
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str(canvas, 0, 62, app->msg_text);
-    }
-}
-
-static void bad_usb_input_callback(InputEvent* input_event, void* ctx) {
-    osMessageQueueId_t event_queue = ctx;
-
-    BadUsbEvent event;
-    event.type = EventTypeInput;
-    event.input = *input_event;
-    osMessageQueuePut(event_queue, &event, 0, osWaitForever);
-}
-
-int32_t bad_usb_app(void* p) {
-    BadUsbParams* app = furi_alloc(sizeof(BadUsbParams));
-    app->event_queue = osMessageQueueNew(8, sizeof(BadUsbEvent), NULL);
-    furi_check(app->event_queue);
-    ViewPort* view_port = view_port_alloc();
-
-    UsbInterface* usb_mode_prev = furi_hal_usb_get_config();
-    furi_hal_usb_set_config(&usb_hid);
-
-    view_port_draw_callback_set(view_port, bad_usb_render_callback, app);
-    view_port_input_callback_set(view_port, bad_usb_input_callback, app->event_queue);
-
-    // Open GUI and register view_port
-    Gui* gui = furi_record_open("gui");
-    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
-    app->thread = NULL;
-    app->thread_attr.name = "bad_usb_worker";
-    app->thread_attr.stack_size = 2048;
-    app->thread = osThreadNew(badusb_worker, app, &app->thread_attr);
-    bool worker_running = true;
-    AppState app_state = AppStateWait;
-    snprintf(app->msg_text, sizeof(app->msg_text), "Press [OK] to start");
-    view_port_update(view_port);
-
-    BadUsbEvent event;
-    while(1) {
-        osStatus_t event_status = osMessageQueueGet(app->event_queue, &event, NULL, osWaitForever);
-
-        if(event_status == osOK) {
-            if(event.type == EventTypeInput) {
-                if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
-                    if(worker_running) {
-                        osThreadFlagsSet(app->thread, WorkerCmdStop);
-                        app_state = AppStateExit;
-                    } else
-                        break;
-                }
-
-                if(event.input.type == InputTypeShort && event.input.key == InputKeyOk) {
-                    if(worker_running) {
-                        app_state = AppStateRunning;
-                        osThreadFlagsSet(app->thread, WorkerCmdStart);
-                        snprintf(app->msg_text, sizeof(app->msg_text), "Running...");
-                        view_port_update(view_port);
-                    }
-                }
-            } else if(event.type == EventTypeWorkerState) {
-                FURI_LOG_I(TAG, "ev: %d", event.worker.state);
-                if(event.worker.state == WorkerStateDone) {
-                    worker_running = false;
-                    if(app_state == AppStateExit)
-                        break;
-                    else if(app_state == AppStateRunning) {
-                        //done
-                        app->thread = osThreadNew(badusb_worker, app, &app->thread_attr);
-                        worker_running = true;
-                        app_state = AppStateWait;
-                        snprintf(app->msg_text, sizeof(app->msg_text), "Press [OK] to start");
-                        view_port_update(view_port);
-                    }
-                } else if(event.worker.state == WorkerStateNoFile) {
-                    app_state = AppStateError;
-                    snprintf(app->msg_text, sizeof(app->msg_text), "File not found!");
-                    view_port_update(view_port);
-                } else if(event.worker.state == WorkerStateScriptError) {
-                    app_state = AppStateError;
-                    snprintf(
-                        app->msg_text,
-                        sizeof(app->msg_text),
-                        "Error at line %u",
-                        event.worker.line);
-                    view_port_update(view_port);
-                }
-            }
-        }
-    }
-    furi_hal_usb_set_config(usb_mode_prev);
-
-    // remove & free all stuff created by app
-    gui_remove_view_port(gui, view_port);
-    view_port_free(view_port);
-
-    osMessageQueueDelete(app->event_queue);
-    free(app);
-
-    return 0;
-}

+ 1 - 0
applications/gpio/usb_uart_bridge.c

@@ -327,6 +327,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) {
 }
 
 void usb_uart_disable(UsbUartBridge* usb_uart) {
+    furi_assert(usb_uart);
     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtStop);
     furi_thread_join(usb_uart->thread);
     furi_thread_free(usb_uart->thread);

+ 11 - 9
applications/gpio/views/gpio_usb_uart.c

@@ -40,9 +40,6 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
     snprintf(temp_str, 18, "Pin %u", model->rx_pin);
     canvas_draw_str(canvas, 22, 42, temp_str);
 
-    canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "B.");
-    canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "B.");
-
     if(model->baudrate == 0)
         snprintf(temp_str, 18, "Baud: ????");
     else
@@ -59,8 +56,8 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
         canvas_set_font(canvas, FontSecondary);
         canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "KB.");
         canvas_set_font(canvas, FontKeyboard);
-        snprintf(temp_str, 18, "%lu", model->tx_cnt);
-        canvas_draw_str_aligned(canvas, 110, 24, AlignRight, AlignBottom, temp_str);
+        snprintf(temp_str, 18, "%lu", model->tx_cnt / 1024);
+        canvas_draw_str_aligned(canvas, 111, 24, AlignRight, AlignBottom, temp_str);
     }
 
     if(model->rx_cnt < 100000000) {
@@ -73,8 +70,8 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
         canvas_set_font(canvas, FontSecondary);
         canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "KB.");
         canvas_set_font(canvas, FontKeyboard);
-        snprintf(temp_str, 18, "%lu", model->rx_cnt);
-        canvas_draw_str_aligned(canvas, 110, 41, AlignRight, AlignBottom, temp_str);
+        snprintf(temp_str, 18, "%lu", model->rx_cnt / 1024);
+        canvas_draw_str_aligned(canvas, 111, 41, AlignRight, AlignBottom, temp_str);
     }
 
     if(model->tx_active)
@@ -130,8 +127,13 @@ View* gpio_usb_uart_get_view(GpioUsbUart* usb_uart) {
 void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context) {
     furi_assert(usb_uart);
     furi_assert(callback);
-    usb_uart->callback = callback;
-    usb_uart->context = context;
+
+    with_view_model(
+        usb_uart->view, (GpioUsbUartModel * model) {
+            usb_uart->callback = callback;
+            usb_uart->context = context;
+            return false;
+        });
 }
 
 void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st) {

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
assets/compiled/assets_icons.c


+ 74 - 65
assets/compiled/assets_icons.h

@@ -36,20 +36,20 @@ extern const Icon A_Level3HijackActive_128x51;
 extern const Icon A_Level3Hijack_128x51;
 extern const Icon A_Level3LabActive_128x51;
 extern const Icon A_Level3Lab_128x51;
-extern const Icon I_LevelUp2_04;
 extern const Icon I_LevelUp2_03;
-extern const Icon I_LevelUp2_07;
-extern const Icon I_LevelUp2_05;
 extern const Icon I_LevelUp2_02;
-extern const Icon I_LevelUp2_06;
+extern const Icon I_LevelUp2_05;
+extern const Icon I_LevelUp2_04;
 extern const Icon I_LevelUp2_01;
-extern const Icon I_LevelUp3_07;
+extern const Icon I_LevelUp2_06;
+extern const Icon I_LevelUp2_07;
+extern const Icon I_LevelUp3_05;
+extern const Icon I_LevelUp3_06;
 extern const Icon I_LevelUp3_02;
+extern const Icon I_LevelUp3_07;
 extern const Icon I_LevelUp3_04;
-extern const Icon I_LevelUp3_05;
-extern const Icon I_LevelUp3_01;
 extern const Icon I_LevelUp3_03;
-extern const Icon I_LevelUp3_06;
+extern const Icon I_LevelUp3_01;
 extern const Icon A_LevelUpPending_128x51;
 extern const Icon A_NoSdCard_128x51;
 extern const Icon A_SleepActive_128x51;
@@ -58,71 +58,81 @@ extern const Icon A_TVActive_128x51;
 extern const Icon A_TV_128x51;
 extern const Icon A_WavesActive_128x51;
 extern const Icon A_Waves_128x51;
-extern const Icon I_sub1_10px;
-extern const Icon I_ir_10px;
-extern const Icon I_unknown_10px;
-extern const Icon I_ibutt_10px;
-extern const Icon I_Nfc_10px;
 extern const Icon I_ble_10px;
+extern const Icon I_ibutt_10px;
 extern const Icon I_125_10px;
+extern const Icon I_sub1_10px;
 extern const Icon I_dir_10px;
+extern const Icon I_ir_10px;
+extern const Icon I_Nfc_10px;
+extern const Icon I_unknown_10px;
 extern const Icon I_BLE_Pairing_128x64;
-extern const Icon I_ButtonDown_7x4;
-extern const Icon I_ButtonCenter_7x7;
-extern const Icon I_ButtonLeft_4x7;
-extern const Icon I_ButtonUp_7x4;
-extern const Icon I_DFU_128x50;
-extern const Icon I_ButtonLeftSmall_3x5;
+extern const Icon I_EviSmile2_18x21;
+extern const Icon I_EviSmile1_18x21;
+extern const Icon I_UsbTree_48x22;
+extern const Icon I_EviWaiting1_18x21;
+extern const Icon I_EviWaiting2_18x21;
+extern const Icon I_Percent_10x14;
+extern const Icon I_Smile_18x18;
+extern const Icon I_Error_18x18;
+extern const Icon I_Clock_18x18;
 extern const Icon I_ButtonRightSmall_3x5;
+extern const Icon I_ButtonLeftSmall_3x5;
+extern const Icon I_ButtonCenter_7x7;
+extern const Icon I_ButtonDown_7x4;
 extern const Icon I_ButtonRight_4x7;
+extern const Icon I_DFU_128x50;
+extern const Icon I_ButtonUp_7x4;
 extern const Icon I_Warning_30x23;
-extern const Icon I_DolphinFirstStart2_59x51;
+extern const Icon I_ButtonLeft_4x7;
+extern const Icon I_DolphinFirstStart7_61x51;
+extern const Icon I_DolphinOkay_41x43;
 extern const Icon I_DolphinFirstStart5_54x49;
-extern const Icon I_DolphinFirstStart6_58x54;
 extern const Icon I_Flipper_young_80x60;
+extern const Icon I_DolphinFirstStart2_59x51;
 extern const Icon I_DolphinFirstStart8_56x51;
-extern const Icon I_DolphinFirstStart1_59x53;
-extern const Icon I_DolphinOkay_41x43;
 extern const Icon I_DolphinFirstStart3_57x48;
-extern const Icon I_DolphinFirstStart7_61x51;
 extern const Icon I_DolphinFirstStart0_70x53;
 extern const Icon I_DolphinFirstStart4_67x53;
+extern const Icon I_DolphinFirstStart6_58x54;
+extern const Icon I_DolphinFirstStart1_59x53;
+extern const Icon I_ArrowDownFilled_14x15;
 extern const Icon I_ArrowUpEmpty_14x15;
 extern const Icon I_ArrowUpFilled_14x15;
-extern const Icon I_ArrowDownFilled_14x15;
 extern const Icon I_ArrowDownEmpty_14x15;
-extern const Icon I_PassportBottom_128x17;
-extern const Icon I_DoorLeft_70x55;
-extern const Icon I_DoorRight_70x55;
 extern const Icon I_DoorLocked_10x56;
+extern const Icon I_PassportBottom_128x17;
 extern const Icon I_PassportLeft_6x47;
+extern const Icon I_DoorLeft_70x55;
 extern const Icon I_LockPopup_100x49;
+extern const Icon I_DoorRight_70x55;
+extern const Icon I_IrdaArrowDown_4x8;
+extern const Icon I_Power_25x27;
+extern const Icon I_Mute_25x27;
 extern const Icon I_Down_hvr_25x27;
-extern const Icon I_Vol_down_hvr_25x27;
-extern const Icon I_Down_25x27;
-extern const Icon I_Fill_marker_7x7;
-extern const Icon I_Vol_down_25x27;
 extern const Icon I_Vol_up_25x27;
-extern const Icon I_Up_hvr_25x27;
-extern const Icon I_Vol_up_hvr_25x27;
 extern const Icon I_IrdaLearnShort_128x31;
-extern const Icon I_IrdaSend_128x64;
-extern const Icon I_DolphinReadingSuccess_59x63;
-extern const Icon I_Mute_hvr_25x27;
-extern const Icon I_Back_15x10;
 extern const Icon I_Up_25x27;
+extern const Icon I_Vol_down_hvr_25x27;
+extern const Icon I_Vol_down_25x27;
+extern const Icon I_Vol_up_hvr_25x27;
+extern const Icon I_Fill_marker_7x7;
+extern const Icon I_Up_hvr_25x27;
 extern const Icon I_IrdaArrowUp_4x8;
-extern const Icon I_Mute_25x27;
-extern const Icon I_Power_25x27;
+extern const Icon I_Down_25x27;
+extern const Icon I_DolphinReadingSuccess_59x63;
 extern const Icon I_IrdaSendShort_128x34;
-extern const Icon I_IrdaArrowDown_4x8;
 extern const Icon I_IrdaLearn_128x64;
+extern const Icon I_Mute_hvr_25x27;
+extern const Icon I_IrdaSend_128x64;
 extern const Icon I_Power_hvr_25x27;
+extern const Icon I_Back_15x10;
 extern const Icon I_KeySaveSelected_24x11;
-extern const Icon I_KeyBackspace_16x9;
-extern const Icon I_KeyBackspaceSelected_16x9;
 extern const Icon I_KeySave_24x11;
+extern const Icon I_KeyBackspaceSelected_16x9;
+extern const Icon I_KeyBackspace_16x9;
 extern const Icon A_125khz_14;
+extern const Icon A_BadUsb_14;
 extern const Icon A_Bluetooth_14;
 extern const Icon A_Debug_14;
 extern const Icon A_FileManager_14;
@@ -138,46 +148,45 @@ extern const Icon A_Sub1ghz_14;
 extern const Icon A_Tamagotchi_14;
 extern const Icon A_U2F_14;
 extern const Icon A_iButton_14;
-extern const Icon I_Detailed_chip_17x13;
 extern const Icon I_Medium_chip_22x21;
+extern const Icon I_Detailed_chip_17x13;
+extern const Icon I_Health_16x16;
+extern const Icon I_Voltage_16x16;
 extern const Icon I_BatteryBody_52x28;
+extern const Icon I_FaceNormal_29x14;
 extern const Icon I_FaceCharging_29x14;
-extern const Icon I_Health_16x16;
-extern const Icon I_Temperature_16x16;
 extern const Icon I_Battery_16x16;
 extern const Icon I_FaceConfused_29x14;
-extern const Icon I_FaceNormal_29x14;
-extern const Icon I_Voltage_16x16;
+extern const Icon I_Temperature_16x16;
 extern const Icon I_FaceNopower_29x14;
-extern const Icon I_RFIDDolphinSend_97x61;
 extern const Icon I_RFIDDolphinSuccess_108x57;
-extern const Icon I_RFIDDolphinReceive_97x61;
 extern const Icon I_RFIDBigChip_37x36;
-extern const Icon I_SDQuestion_35x43;
+extern const Icon I_RFIDDolphinReceive_97x61;
+extern const Icon I_RFIDDolphinSend_97x61;
 extern const Icon I_SDError_43x35;
+extern const Icon I_SDQuestion_35x43;
 extern const Icon I_Cry_dolph_55x52;
-extern const Icon I_Background_128x11;
-extern const Icon I_Lock_8x8;
-extern const Icon I_Battery_26x8;
 extern const Icon I_Battery_19x8;
-extern const Icon I_USBConnected_15x8;
-extern const Icon I_Background_128x8;
-extern const Icon I_BadUsb_9x8;
-extern const Icon I_BT_Pair_9x8;
-extern const Icon I_PlaceholderL_11x13;
 extern const Icon I_SDcardFail_11x8;
 extern const Icon I_Bluetooth_5x8;
 extern const Icon I_PlaceholderR_30x13;
+extern const Icon I_Battery_26x8;
+extern const Icon I_Lock_8x8;
 extern const Icon I_SDcardMounted_11x8;
+extern const Icon I_BadUsb_9x8;
+extern const Icon I_BT_Pair_9x8;
+extern const Icon I_PlaceholderL_11x13;
+extern const Icon I_Background_128x11;
+extern const Icon I_USBConnected_15x8;
 extern const Icon I_Quest_7x8;
-extern const Icon I_Lock_7x8;
-extern const Icon I_Scanning_123x52;
 extern const Icon I_MHz_25x11;
+extern const Icon I_Scanning_123x52;
 extern const Icon I_Unlock_7x8;
-extern const Icon I_iButtonKey_49x44;
+extern const Icon I_Lock_7x8;
+extern const Icon I_DolphinNice_96x59;
+extern const Icon I_iButtonDolphinSuccess_109x60;
 extern const Icon I_DolphinExcited_64x63;
-extern const Icon I_DolphinWait_61x59;
+extern const Icon I_iButtonKey_49x44;
 extern const Icon I_iButtonDolphinVerySuccess_108x52;
+extern const Icon I_DolphinWait_61x59;
 extern const Icon I_DolphinMafia_115x62;
-extern const Icon I_DolphinNice_96x59;
-extern const Icon I_iButtonDolphinSuccess_109x60;

BIN
assets/icons/BadUsb/Clock_18x18.png


BIN
assets/icons/BadUsb/Error_18x18.png


BIN
assets/icons/BadUsb/EviSmile1_18x21.png


BIN
assets/icons/BadUsb/EviSmile2_18x21.png


BIN
assets/icons/BadUsb/EviWaiting1_18x21.png


BIN
assets/icons/BadUsb/EviWaiting2_18x21.png


BIN
assets/icons/BadUsb/Percent_10x14.png


BIN
assets/icons/BadUsb/Smile_18x18.png


BIN
assets/icons/BadUsb/UsbTree_48x22.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_01.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_02.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_03.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_04.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_05.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_06.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_07.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_08.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_09.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_10.png


BIN
assets/icons/MainMenu/BadUsb_14/frame_11.png


+ 1 - 0
assets/icons/MainMenu/BadUsb_14/frame_rate

@@ -0,0 +1 @@
+3

+ 58 - 34
firmware/targets/f6/furi-hal/furi-hal-usb-hid.c

@@ -1,6 +1,7 @@
 #include "furi-hal-version.h"
 #include "furi-hal-usb_i.h"
 #include "furi-hal-usb.h"
+#include "furi-hal-usb-hid.h"
 #include <furi.h>
 
 #include "usb.h"
@@ -37,24 +38,24 @@ static const uint8_t hid_report_desc[] = {
     HID_USAGE(HID_DESKTOP_KEYBOARD),
     HID_COLLECTION(HID_APPLICATION_COLLECTION),
         HID_REPORT_ID(ReportIdKeyboard),
-        HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
-        HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
-        HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
-        HID_LOGICAL_MINIMUM(0),
-        HID_LOGICAL_MAXIMUM(1),
-        HID_REPORT_SIZE(1),
-        HID_REPORT_COUNT(8),
+            HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
+            HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
+            HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
+            HID_LOGICAL_MINIMUM(0),
+            HID_LOGICAL_MAXIMUM(1),
+            HID_REPORT_SIZE(1),
+            HID_REPORT_COUNT(8),
         HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-        HID_REPORT_COUNT(1),
-        HID_REPORT_SIZE(8),
+            HID_REPORT_COUNT(1),
+            HID_REPORT_SIZE(8),
         HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-        HID_REPORT_COUNT(6),
-        HID_REPORT_SIZE(8),
-        HID_LOGICAL_MINIMUM(0),
-        HID_LOGICAL_MAXIMUM(101),
-        HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
-        HID_USAGE_MINIMUM(0),
-        HID_USAGE_MAXIMUM(101),
+            HID_REPORT_COUNT(6),
+            HID_REPORT_SIZE(8),
+            HID_LOGICAL_MINIMUM(0),
+            HID_LOGICAL_MAXIMUM(101),
+            HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
+            HID_USAGE_MINIMUM(0),
+            HID_USAGE_MAXIMUM(101),
         HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
     HID_END_COLLECTION,
     HID_USAGE_PAGE(HID_PAGE_DESKTOP),
@@ -63,25 +64,25 @@ static const uint8_t hid_report_desc[] = {
         HID_USAGE(HID_DESKTOP_POINTER),
         HID_COLLECTION(HID_PHYSICAL_COLLECTION),
             HID_REPORT_ID(ReportIdMouse),
-            HID_USAGE_PAGE(HID_PAGE_BUTTON),
-            HID_USAGE_MINIMUM(1),
-            HID_USAGE_MAXIMUM(3),
-            HID_LOGICAL_MINIMUM(0),
-            HID_LOGICAL_MAXIMUM(1),
-            HID_REPORT_COUNT(3),
-            HID_REPORT_SIZE(1),
+                HID_USAGE_PAGE(HID_PAGE_BUTTON),
+                HID_USAGE_MINIMUM(1),
+                HID_USAGE_MAXIMUM(3),
+                HID_LOGICAL_MINIMUM(0),
+                HID_LOGICAL_MAXIMUM(1),
+                HID_REPORT_COUNT(3),
+                HID_REPORT_SIZE(1),
             HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-            HID_REPORT_SIZE(1),
-            HID_REPORT_COUNT(5),
+                HID_REPORT_SIZE(1),
+                HID_REPORT_COUNT(5),
             HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-            HID_USAGE_PAGE(HID_PAGE_DESKTOP),
-            HID_USAGE(HID_DESKTOP_X),
-            HID_USAGE(HID_DESKTOP_Y),
-            HID_USAGE(HID_DESKTOP_WHEEL),
-            HID_LOGICAL_MINIMUM(-127),
-            HID_LOGICAL_MAXIMUM(127),
-            HID_REPORT_SIZE(8),
-            HID_REPORT_COUNT(3),
+                HID_USAGE_PAGE(HID_PAGE_DESKTOP),
+                HID_USAGE(HID_DESKTOP_X),
+                HID_USAGE(HID_DESKTOP_Y),
+                HID_USAGE(HID_DESKTOP_WHEEL),
+                HID_LOGICAL_MINIMUM(-127),
+                HID_LOGICAL_MAXIMUM(127),
+                HID_REPORT_SIZE(8),
+                HID_REPORT_COUNT(3),
             HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
         HID_END_COLLECTION,
     HID_END_COLLECTION,
@@ -194,11 +195,28 @@ static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_ca
 static usbd_device* usb_dev;
 static osSemaphoreId_t hid_semaphore = NULL;
 static bool hid_connected = false;
+static HidStateCallback callback;
+static void* cb_ctx;
 
 bool furi_hal_hid_is_connected() {
     return hid_connected;
 }
 
+void furi_hal_hid_set_state_callback(HidStateCallback cb, void* ctx) {
+    if (callback != NULL) {
+        if (hid_connected == true)
+            callback(false, cb_ctx);
+    }
+
+    callback = cb;
+    cb_ctx = ctx;
+
+    if (callback != NULL) {
+        if (hid_connected == true)
+            callback(true, cb_ctx);
+    }
+}
+
 bool furi_hal_hid_kb_press(uint16_t button) {
     for (uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
         if (hid_report.keyboard.btn[key_nb] == 0) {
@@ -289,13 +307,19 @@ static void hid_deinit(usbd_device *dev) {
 }
 
 static void hid_on_wakeup(usbd_device *dev) {
-    hid_connected = true;
+    if (hid_connected == false) {
+        hid_connected = true;
+        if (callback != NULL)
+            callback(true, cb_ctx);
+    }
 }
 
 static void hid_on_suspend(usbd_device *dev) {
     if (hid_connected == true) {
         hid_connected = false;
         osSemaphoreRelease(hid_semaphore);
+        if (callback != NULL)
+            callback(false, cb_ctx);
     }
 }
 

+ 58 - 34
firmware/targets/f7/furi-hal/furi-hal-usb-hid.c

@@ -1,6 +1,7 @@
 #include "furi-hal-version.h"
 #include "furi-hal-usb_i.h"
 #include "furi-hal-usb.h"
+#include "furi-hal-usb-hid.h"
 #include <furi.h>
 
 #include "usb.h"
@@ -37,24 +38,24 @@ static const uint8_t hid_report_desc[] = {
     HID_USAGE(HID_DESKTOP_KEYBOARD),
     HID_COLLECTION(HID_APPLICATION_COLLECTION),
         HID_REPORT_ID(ReportIdKeyboard),
-        HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
-        HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
-        HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
-        HID_LOGICAL_MINIMUM(0),
-        HID_LOGICAL_MAXIMUM(1),
-        HID_REPORT_SIZE(1),
-        HID_REPORT_COUNT(8),
+            HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
+            HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
+            HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
+            HID_LOGICAL_MINIMUM(0),
+            HID_LOGICAL_MAXIMUM(1),
+            HID_REPORT_SIZE(1),
+            HID_REPORT_COUNT(8),
         HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-        HID_REPORT_COUNT(1),
-        HID_REPORT_SIZE(8),
+            HID_REPORT_COUNT(1),
+            HID_REPORT_SIZE(8),
         HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-        HID_REPORT_COUNT(6),
-        HID_REPORT_SIZE(8),
-        HID_LOGICAL_MINIMUM(0),
-        HID_LOGICAL_MAXIMUM(101),
-        HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
-        HID_USAGE_MINIMUM(0),
-        HID_USAGE_MAXIMUM(101),
+            HID_REPORT_COUNT(6),
+            HID_REPORT_SIZE(8),
+            HID_LOGICAL_MINIMUM(0),
+            HID_LOGICAL_MAXIMUM(101),
+            HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
+            HID_USAGE_MINIMUM(0),
+            HID_USAGE_MAXIMUM(101),
         HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
     HID_END_COLLECTION,
     HID_USAGE_PAGE(HID_PAGE_DESKTOP),
@@ -63,25 +64,25 @@ static const uint8_t hid_report_desc[] = {
         HID_USAGE(HID_DESKTOP_POINTER),
         HID_COLLECTION(HID_PHYSICAL_COLLECTION),
             HID_REPORT_ID(ReportIdMouse),
-            HID_USAGE_PAGE(HID_PAGE_BUTTON),
-            HID_USAGE_MINIMUM(1),
-            HID_USAGE_MAXIMUM(3),
-            HID_LOGICAL_MINIMUM(0),
-            HID_LOGICAL_MAXIMUM(1),
-            HID_REPORT_COUNT(3),
-            HID_REPORT_SIZE(1),
+                HID_USAGE_PAGE(HID_PAGE_BUTTON),
+                HID_USAGE_MINIMUM(1),
+                HID_USAGE_MAXIMUM(3),
+                HID_LOGICAL_MINIMUM(0),
+                HID_LOGICAL_MAXIMUM(1),
+                HID_REPORT_COUNT(3),
+                HID_REPORT_SIZE(1),
             HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-            HID_REPORT_SIZE(1),
-            HID_REPORT_COUNT(5),
+                HID_REPORT_SIZE(1),
+                HID_REPORT_COUNT(5),
             HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-            HID_USAGE_PAGE(HID_PAGE_DESKTOP),
-            HID_USAGE(HID_DESKTOP_X),
-            HID_USAGE(HID_DESKTOP_Y),
-            HID_USAGE(HID_DESKTOP_WHEEL),
-            HID_LOGICAL_MINIMUM(-127),
-            HID_LOGICAL_MAXIMUM(127),
-            HID_REPORT_SIZE(8),
-            HID_REPORT_COUNT(3),
+                HID_USAGE_PAGE(HID_PAGE_DESKTOP),
+                HID_USAGE(HID_DESKTOP_X),
+                HID_USAGE(HID_DESKTOP_Y),
+                HID_USAGE(HID_DESKTOP_WHEEL),
+                HID_LOGICAL_MINIMUM(-127),
+                HID_LOGICAL_MAXIMUM(127),
+                HID_REPORT_SIZE(8),
+                HID_REPORT_COUNT(3),
             HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
         HID_END_COLLECTION,
     HID_END_COLLECTION,
@@ -194,11 +195,28 @@ static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_ca
 static usbd_device* usb_dev;
 static osSemaphoreId_t hid_semaphore = NULL;
 static bool hid_connected = false;
+static HidStateCallback callback;
+static void* cb_ctx;
 
 bool furi_hal_hid_is_connected() {
     return hid_connected;
 }
 
+void furi_hal_hid_set_state_callback(HidStateCallback cb, void* ctx) {
+    if (callback != NULL) {
+        if (hid_connected == true)
+            callback(false, cb_ctx);
+    }
+
+    callback = cb;
+    cb_ctx = ctx;
+
+    if (callback != NULL) {
+        if (hid_connected == true)
+            callback(true, cb_ctx);
+    }
+}
+
 bool furi_hal_hid_kb_press(uint16_t button) {
     for (uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
         if (hid_report.keyboard.btn[key_nb] == 0) {
@@ -289,13 +307,19 @@ static void hid_deinit(usbd_device *dev) {
 }
 
 static void hid_on_wakeup(usbd_device *dev) {
-    hid_connected = true;
+    if (hid_connected == false) {
+        hid_connected = true;
+        if (callback != NULL)
+            callback(true, cb_ctx);
+    }
 }
 
 static void hid_on_suspend(usbd_device *dev) {
     if (hid_connected == true) {
         hid_connected = false;
         osSemaphoreRelease(hid_semaphore);
+        if (callback != NULL)
+            callback(false, cb_ctx);
     }
 }
 

+ 9 - 0
firmware/targets/furi-hal-include/furi-hal-usb-hid.h

@@ -250,6 +250,8 @@ static const uint16_t hid_asciimap[] = {
     KEY_NONE, // DEL
 };
 
+typedef void (*HidStateCallback)(bool state, void* context);
+
 /** ASCII to keycode conversion macro */
 #define HID_ASCII_TO_KEY(x) (((uint8_t)x < 128) ? (hid_asciimap[(uint8_t)x]) : KEY_NONE)
 
@@ -266,6 +268,13 @@ enum HidMouseButtons {
  */
 bool furi_hal_hid_is_connected();
 
+/** Set USB HID connect/disconnect callback
+ *
+ * @param      cb  callback
+ * @param      ctx  callback context
+ */
+void furi_hal_hid_set_state_callback(HidStateCallback cb, void* ctx);
+
 /** Set the following key to pressed state and send HID report
  *
  * @param      button  key code

+ 2 - 0
lib/subghz/subghz_keystore.c

@@ -77,6 +77,8 @@ static bool subghz_keystore_process_line(SubGhzKeystore* instance, char* line) {
 }
 
 static void subghz_keystore_mess_with_iv(uint8_t* iv) {
+    // Alignment check for `ldrd` instruction
+    furi_assert(((uint32_t)iv) % 4 == 0);
     // Please do not share decrypted manufacture keys
     // Sharing them will bring some discomfort to legal owners
     // And potential legal action against you

Некоторые файлы не были показаны из-за большого количества измененных файлов