mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-13 00:07:12 +00:00
Merge remote-tracking branch 'OFW/dev' into dev
This commit is contained in:
commit
7de26fb99b
58 changed files with 580 additions and 2569 deletions
|
@ -7,7 +7,7 @@ App(
|
|||
icon="A_BadUsb_14",
|
||||
order=70,
|
||||
resources="resources",
|
||||
fap_libs=["assets"],
|
||||
fap_libs=["assets", "ble_profile"],
|
||||
fap_icon="icon.png",
|
||||
fap_category="USB",
|
||||
)
|
||||
|
|
|
@ -35,6 +35,7 @@ static void bad_usb_load_settings(BadUsbApp* app) {
|
|||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
uint32_t version = 0;
|
||||
uint32_t interface = 0;
|
||||
|
||||
if(flipper_format_file_open_existing(fff, BAD_USB_SETTINGS_PATH)) {
|
||||
do {
|
||||
|
@ -44,6 +45,8 @@ static void bad_usb_load_settings(BadUsbApp* app) {
|
|||
break;
|
||||
|
||||
if(!flipper_format_read_string(fff, "layout", temp_str)) break;
|
||||
if(!flipper_format_read_uint32(fff, "interface", &interface, 1)) break;
|
||||
if(interface > BadUsbHidInterfaceBle) break;
|
||||
|
||||
state = true;
|
||||
} while(0);
|
||||
|
@ -53,6 +56,7 @@ static void bad_usb_load_settings(BadUsbApp* app) {
|
|||
|
||||
if(state) {
|
||||
furi_string_set(app->keyboard_layout, temp_str);
|
||||
app->interface = interface;
|
||||
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
FileInfo layout_file_info;
|
||||
|
@ -64,6 +68,7 @@ static void bad_usb_load_settings(BadUsbApp* app) {
|
|||
}
|
||||
} else {
|
||||
furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);
|
||||
app->interface = BadUsbHidInterfaceUsb;
|
||||
}
|
||||
|
||||
furi_string_free(temp_str);
|
||||
|
@ -79,6 +84,9 @@ static void bad_usb_save_settings(BadUsbApp* app) {
|
|||
fff, BAD_USB_SETTINGS_FILE_TYPE, BAD_USB_SETTINGS_VERSION))
|
||||
break;
|
||||
if(!flipper_format_write_string(fff, "layout", app->keyboard_layout)) break;
|
||||
uint32_t interface_id = app->interface;
|
||||
if(!flipper_format_write_uint32(fff, "interface", (const uint32_t*)&interface_id, 1))
|
||||
break;
|
||||
} while(0);
|
||||
}
|
||||
|
||||
|
@ -86,6 +94,11 @@ static void bad_usb_save_settings(BadUsbApp* app) {
|
|||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface) {
|
||||
app->interface = interface;
|
||||
bad_usb_view_set_interface(app->bad_usb_view, interface);
|
||||
}
|
||||
|
||||
BadUsbApp* bad_usb_app_alloc(char* arg) {
|
||||
BadUsbApp* app = malloc(sizeof(BadUsbApp));
|
||||
|
||||
|
@ -117,7 +130,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
|
|||
// Custom Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget));
|
||||
app->view_dispatcher, BadUsbAppViewWidget, widget_get_view(app->widget));
|
||||
|
||||
// Popup
|
||||
app->popup = popup_alloc();
|
||||
view_dispatcher_add_view(app->view_dispatcher, BadUsbAppViewPopup, popup_get_view(app->popup));
|
||||
|
||||
app->var_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
|
@ -163,9 +180,13 @@ void bad_usb_app_free(BadUsbApp* app) {
|
|||
bad_usb_view_free(app->bad_usb_view);
|
||||
|
||||
// Custom Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
// Popup
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewPopup);
|
||||
popup_free(app->popup);
|
||||
|
||||
// Config menu
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig);
|
||||
variable_item_list_free(app->var_item_list);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <notification/notification_messages.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include "views/bad_usb_view.h"
|
||||
#include <furi_hal_usb.h>
|
||||
|
||||
|
@ -33,6 +34,7 @@ struct BadUsbApp {
|
|||
NotificationApp* notifications;
|
||||
DialogsApp* dialogs;
|
||||
Widget* widget;
|
||||
Popup* popup;
|
||||
VariableItemList* var_item_list;
|
||||
|
||||
BadUsbAppError error;
|
||||
|
@ -41,11 +43,15 @@ struct BadUsbApp {
|
|||
BadUsb* bad_usb_view;
|
||||
BadUsbScript* bad_usb_script;
|
||||
|
||||
BadUsbHidInterface interface;
|
||||
FuriHalUsbInterface* usb_if_prev;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
BadUsbAppViewError,
|
||||
BadUsbAppViewWidget,
|
||||
BadUsbAppViewPopup,
|
||||
BadUsbAppViewWork,
|
||||
BadUsbAppViewConfig,
|
||||
} BadUsbAppView;
|
||||
|
||||
void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface);
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#include "bad_usb_hid.h"
|
||||
#include <extra_profiles/hid_profile.h>
|
||||
#include <bt/bt_service/bt.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "BadUSB HID"
|
||||
|
||||
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
|
||||
|
||||
void* hid_usb_init(FuriHalUsbHidConfig* hid_cfg) {
|
||||
furi_check(furi_hal_usb_set_config(&usb_hid, hid_cfg));
|
||||
return NULL;
|
||||
|
@ -69,6 +72,155 @@ static const BadUsbHidApi hid_api_usb = {
|
|||
.release_all = hid_usb_release_all,
|
||||
.get_led_state = hid_usb_get_led_state,
|
||||
};
|
||||
const BadUsbHidApi* bad_usb_hid_get_interface() {
|
||||
return &hid_api_usb;
|
||||
|
||||
typedef struct {
|
||||
Bt* bt;
|
||||
FuriHalBleProfileBase* profile;
|
||||
HidStateCallback state_callback;
|
||||
void* callback_context;
|
||||
bool is_connected;
|
||||
} BleHidInstance;
|
||||
|
||||
static const BleProfileHidParams ble_hid_params = {
|
||||
.device_name_prefix = "BadUSB",
|
||||
.mac_xor = 0x0002,
|
||||
};
|
||||
|
||||
static void hid_ble_connection_status_callback(BtStatus status, void* context) {
|
||||
furi_assert(context);
|
||||
BleHidInstance* ble_hid = context;
|
||||
ble_hid->is_connected = (status == BtStatusConnected);
|
||||
if(ble_hid->state_callback) {
|
||||
ble_hid->state_callback(ble_hid->is_connected, ble_hid->callback_context);
|
||||
}
|
||||
}
|
||||
|
||||
void* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) {
|
||||
UNUSED(hid_cfg);
|
||||
BleHidInstance* ble_hid = malloc(sizeof(BleHidInstance));
|
||||
ble_hid->bt = furi_record_open(RECORD_BT);
|
||||
bt_disconnect(ble_hid->bt);
|
||||
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
|
||||
bt_keys_storage_set_storage_path(ble_hid->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
|
||||
|
||||
ble_hid->profile = bt_profile_start(ble_hid->bt, ble_profile_hid, (void*)&ble_hid_params);
|
||||
furi_check(ble_hid->profile);
|
||||
|
||||
furi_hal_bt_start_advertising();
|
||||
|
||||
bt_set_status_changed_callback(ble_hid->bt, hid_ble_connection_status_callback, ble_hid);
|
||||
|
||||
return ble_hid;
|
||||
}
|
||||
|
||||
void hid_ble_deinit(void* inst) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
|
||||
bt_set_status_changed_callback(ble_hid->bt, NULL, NULL);
|
||||
bt_disconnect(ble_hid->bt);
|
||||
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
bt_keys_storage_set_default_path(ble_hid->bt);
|
||||
|
||||
furi_check(bt_profile_restore_default(ble_hid->bt));
|
||||
furi_record_close(RECORD_BT);
|
||||
free(ble_hid);
|
||||
}
|
||||
|
||||
void hid_ble_set_state_callback(void* inst, HidStateCallback cb, void* context) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
ble_hid->state_callback = cb;
|
||||
ble_hid->callback_context = context;
|
||||
}
|
||||
|
||||
bool hid_ble_is_connected(void* inst) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_hid->is_connected;
|
||||
}
|
||||
|
||||
bool hid_ble_kb_press(void* inst, uint16_t button) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_profile_hid_kb_press(ble_hid->profile, button);
|
||||
}
|
||||
|
||||
bool hid_ble_kb_release(void* inst, uint16_t button) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_profile_hid_kb_release(ble_hid->profile, button);
|
||||
}
|
||||
|
||||
bool hid_ble_consumer_press(void* inst, uint16_t button) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_profile_hid_consumer_key_press(ble_hid->profile, button);
|
||||
}
|
||||
|
||||
bool hid_ble_consumer_release(void* inst, uint16_t button) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_profile_hid_consumer_key_release(ble_hid->profile, button);
|
||||
}
|
||||
|
||||
bool hid_ble_release_all(void* inst) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
bool state = ble_profile_hid_kb_release_all(ble_hid->profile);
|
||||
state &= ble_profile_hid_consumer_key_release_all(ble_hid->profile);
|
||||
return state;
|
||||
}
|
||||
|
||||
uint8_t hid_ble_get_led_state(void* inst) {
|
||||
UNUSED(inst);
|
||||
FURI_LOG_W(TAG, "hid_ble_get_led_state not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const BadUsbHidApi hid_api_ble = {
|
||||
.init = hid_ble_init,
|
||||
.deinit = hid_ble_deinit,
|
||||
.set_state_callback = hid_ble_set_state_callback,
|
||||
.is_connected = hid_ble_is_connected,
|
||||
|
||||
.kb_press = hid_ble_kb_press,
|
||||
.kb_release = hid_ble_kb_release,
|
||||
.consumer_press = hid_ble_consumer_press,
|
||||
.consumer_release = hid_ble_consumer_release,
|
||||
.release_all = hid_ble_release_all,
|
||||
.get_led_state = hid_ble_get_led_state,
|
||||
};
|
||||
|
||||
const BadUsbHidApi* bad_usb_hid_get_interface(BadUsbHidInterface interface) {
|
||||
if(interface == BadUsbHidInterfaceUsb) {
|
||||
return &hid_api_usb;
|
||||
} else {
|
||||
return &hid_api_ble;
|
||||
}
|
||||
}
|
||||
|
||||
void bad_usb_hid_ble_remove_pairing(void) {
|
||||
Bt* bt = furi_record_open(RECORD_BT);
|
||||
bt_disconnect(bt);
|
||||
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
|
||||
furi_hal_bt_stop_advertising();
|
||||
|
||||
bt_keys_storage_set_storage_path(bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
|
||||
bt_forget_bonded_devices(bt);
|
||||
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
bt_keys_storage_set_default_path(bt);
|
||||
|
||||
furi_check(bt_profile_restore_default(bt));
|
||||
furi_record_close(RECORD_BT);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,11 @@ extern "C" {
|
|||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef enum {
|
||||
BadUsbHidInterfaceUsb,
|
||||
BadUsbHidInterfaceBle,
|
||||
} BadUsbHidInterface;
|
||||
|
||||
typedef struct {
|
||||
void* (*init)(FuriHalUsbHidConfig* hid_cfg);
|
||||
void (*deinit)(void* inst);
|
||||
|
@ -21,7 +26,7 @@ typedef struct {
|
|||
uint8_t (*get_led_state)(void* inst);
|
||||
} BadUsbHidApi;
|
||||
|
||||
const BadUsbHidApi* bad_usb_hid_get_interface();
|
||||
const BadUsbHidApi* bad_usb_hid_get_interface(BadUsbHidInterface interface);
|
||||
|
||||
void bad_usb_hid_ble_remove_pairing(void);
|
||||
|
||||
|
|
|
@ -654,7 +654,7 @@ static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) {
|
|||
memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout)));
|
||||
}
|
||||
|
||||
BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
||||
BadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface interface) {
|
||||
furi_assert(file_path);
|
||||
|
||||
BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
|
||||
|
@ -664,7 +664,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
|||
|
||||
bad_usb->st.state = BadUsbStateInit;
|
||||
bad_usb->st.error[0] = '\0';
|
||||
bad_usb->hid = bad_usb_hid_get_interface();
|
||||
bad_usb->hid = bad_usb_hid_get_interface(interface);
|
||||
|
||||
bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
|
||||
furi_thread_start(bad_usb->thread);
|
||||
|
|
|
@ -34,7 +34,7 @@ typedef struct {
|
|||
|
||||
typedef struct BadUsbScript BadUsbScript;
|
||||
|
||||
BadUsbScript* bad_usb_script_open(FuriString* file_path);
|
||||
BadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface interface);
|
||||
|
||||
void bad_usb_script_close(BadUsbScript* bad_usb);
|
||||
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
REM This is BadUSB demo script for ChromeOS by kowalski7cc
|
||||
REM This is BadUSB demo script for Chrome and ChromeOS by kowalski7cc
|
||||
|
||||
REM Exit from Overview
|
||||
ESC
|
||||
REM Open a new tab
|
||||
CTRL t
|
||||
REM wait for some slower chromebooks
|
||||
DELAY 1000
|
||||
REM Make sure we have omnibox focus
|
||||
CTRL l
|
||||
DELAY 200
|
||||
REM Open an empty editable page
|
||||
DEFAULT_DELAY 50
|
||||
STRING data:text/html, <html contenteditable autofocus><style>body{font-family:monospace;}
|
||||
STRING data:text/html, <html contenteditable autofocus><title>Flipper Zero BadUSB Demo</title><style>body{font-family:monospace;}
|
||||
ENTER
|
||||
DELAY 500
|
||||
|
||||
|
|
|
@ -5,14 +5,22 @@ REM Check the `lsusb` command to know your own devices IDs
|
|||
|
||||
REM This is BadUSB demo script for Linux/Gnome
|
||||
|
||||
REM Exit from Overview
|
||||
ESC
|
||||
DELAY 200
|
||||
REM Open terminal window
|
||||
DELAY 1000
|
||||
ALT F2
|
||||
DELAY 500
|
||||
STRING gnome-terminal --maximize
|
||||
DELAY 500
|
||||
DELAY 1000
|
||||
REM Let's guess user terminal, based on (almost) glib order with ptyxis now default in Fedora 41
|
||||
STRING sh -c "xdg-terminal-exec||kgx||ptyxis||gnome-terminal||mate-terminal||xfce4-terminal||tilix||konsole||xterm"
|
||||
DELAY 300
|
||||
ENTER
|
||||
REM It can take a bit to open the correct terminal
|
||||
DELAY 1500
|
||||
|
||||
REM Make sure we are running in a POSIX-compliant shell
|
||||
STRING env sh
|
||||
ENTER
|
||||
DELAY 750
|
||||
|
||||
REM Clear the screen in case some banner was displayed
|
||||
STRING clear
|
||||
|
|
59
applications/main/bad_usb/scenes/bad_usb_scene_config.c
Normal file
59
applications/main/bad_usb/scenes/bad_usb_scene_config.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "../bad_usb_app_i.h"
|
||||
|
||||
enum SubmenuIndex {
|
||||
ConfigIndexKeyboardLayout,
|
||||
ConfigIndexBleUnpair,
|
||||
};
|
||||
|
||||
void bad_usb_scene_config_select_callback(void* context, uint32_t index) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
|
||||
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index);
|
||||
}
|
||||
|
||||
static void draw_menu(BadUsbApp* bad_usb) {
|
||||
VariableItemList* var_item_list = bad_usb->var_item_list;
|
||||
|
||||
variable_item_list_reset(var_item_list);
|
||||
|
||||
variable_item_list_add(var_item_list, "Keyboard Layout (global)", 0, NULL, NULL);
|
||||
|
||||
variable_item_list_add(var_item_list, "Remove Pairing", 0, NULL, NULL);
|
||||
}
|
||||
|
||||
void bad_usb_scene_config_on_enter(void* context) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
VariableItemList* var_item_list = bad_usb->var_item_list;
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
var_item_list, bad_usb_scene_config_select_callback, bad_usb);
|
||||
draw_menu(bad_usb);
|
||||
variable_item_list_set_selected_item(var_item_list, 0);
|
||||
|
||||
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig);
|
||||
}
|
||||
|
||||
bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == ConfigIndexKeyboardLayout) {
|
||||
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout);
|
||||
} else if(event.event == ConfigIndexBleUnpair) {
|
||||
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfirmUnpair);
|
||||
} else {
|
||||
furi_crash("Unknown key type");
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_usb_scene_config_on_exit(void* context) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
VariableItemList* var_item_list = bad_usb->var_item_list;
|
||||
|
||||
variable_item_list_reset(var_item_list);
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
ADD_SCENE(bad_usb, file_select, FileSelect)
|
||||
ADD_SCENE(bad_usb, work, Work)
|
||||
ADD_SCENE(bad_usb, error, Error)
|
||||
ADD_SCENE(bad_usb, config, Config)
|
||||
ADD_SCENE(bad_usb, config_layout, ConfigLayout)
|
||||
ADD_SCENE(bad_usb, confirm_unpair, ConfirmUnpair)
|
||||
ADD_SCENE(bad_usb, unpair_done, UnpairDone)
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
#include "../bad_ble_app_i.h"
|
||||
#include "../bad_usb_app_i.h"
|
||||
|
||||
void bad_ble_scene_confirm_unpair_widget_callback(
|
||||
void bad_usb_scene_confirm_unpair_widget_callback(
|
||||
GuiButtonType type,
|
||||
InputType input_type,
|
||||
void* context) {
|
||||
UNUSED(input_type);
|
||||
SceneManagerEvent event = {.type = SceneManagerEventTypeCustom, .event = type};
|
||||
bad_ble_scene_confirm_unpair_on_event(context, event);
|
||||
bad_usb_scene_confirm_unpair_on_event(context, event);
|
||||
}
|
||||
|
||||
void bad_ble_scene_confirm_unpair_on_enter(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
Widget* widget = bad_ble->widget;
|
||||
void bad_usb_scene_confirm_unpair_on_enter(void* context) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
Widget* widget = bad_usb->widget;
|
||||
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Cancel", bad_ble_scene_confirm_unpair_widget_callback, context);
|
||||
widget, GuiButtonTypeLeft, "Cancel", bad_usb_scene_confirm_unpair_widget_callback, context);
|
||||
widget_add_button_element(
|
||||
widget,
|
||||
GuiButtonTypeRight,
|
||||
"Unpair",
|
||||
bad_ble_scene_confirm_unpair_widget_callback,
|
||||
bad_usb_scene_confirm_unpair_widget_callback,
|
||||
context);
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 64, AlignCenter, AlignTop, "\e#Unpair the Device?\e#\n", false);
|
||||
|
||||
view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewWidget);
|
||||
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewWidget);
|
||||
}
|
||||
|
||||
bool bad_ble_scene_confirm_unpair_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBleApp* bad_ble = context;
|
||||
SceneManager* scene_manager = bad_ble->scene_manager;
|
||||
bool bad_usb_scene_confirm_unpair_on_event(void* context, SceneManagerEvent event) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
SceneManager* scene_manager = bad_usb->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(scene_manager, BadBleSceneUnpairDone);
|
||||
scene_manager_next_scene(scene_manager, BadUsbSceneUnpairDone);
|
||||
} else if(event.event == GuiButtonTypeLeft) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ bool bad_ble_scene_confirm_unpair_on_event(void* context, SceneManagerEvent even
|
|||
return consumed;
|
||||
}
|
||||
|
||||
void bad_ble_scene_confirm_unpair_on_exit(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
Widget* widget = bad_ble->widget;
|
||||
void bad_usb_scene_confirm_unpair_on_exit(void* context) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
Widget* widget = bad_usb->widget;
|
||||
|
||||
widget_reset(widget);
|
||||
}
|
|
@ -43,7 +43,7 @@ void bad_usb_scene_error_on_enter(void* context) {
|
|||
"Disconnect from\nPC or phone to\nuse this function.");
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewError);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWidget);
|
||||
}
|
||||
|
||||
bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) {
|
||||
|
|
39
applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c
Normal file
39
applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "../bad_usb_app_i.h"
|
||||
|
||||
static void bad_usb_scene_unpair_done_popup_callback(void* context) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneConfig);
|
||||
}
|
||||
|
||||
void bad_usb_scene_unpair_done_on_enter(void* context) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
Popup* popup = bad_usb->popup;
|
||||
|
||||
bad_usb_hid_ble_remove_pairing();
|
||||
|
||||
popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58);
|
||||
popup_set_header(popup, "Done", 20, 19, AlignLeft, AlignBottom);
|
||||
popup_set_callback(popup, bad_usb_scene_unpair_done_popup_callback);
|
||||
popup_set_context(popup, bad_usb);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewPopup);
|
||||
}
|
||||
|
||||
bool bad_usb_scene_unpair_done_on_event(void* context, SceneManagerEvent event) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
UNUSED(bad_usb);
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_usb_scene_unpair_done_on_exit(void* context) {
|
||||
BadUsbApp* bad_usb = context;
|
||||
Popup* popup = bad_usb->popup;
|
||||
UNUSED(popup);
|
||||
|
||||
popup_reset(popup);
|
||||
}
|
|
@ -20,14 +20,27 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
|
|||
bad_usb_script_close(app->bad_usb_script);
|
||||
app->bad_usb_script = NULL;
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigLayout);
|
||||
if(app->interface == BadUsbHidInterfaceBle) {
|
||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
|
||||
} else {
|
||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigLayout);
|
||||
}
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == InputKeyOk) {
|
||||
bad_usb_script_start_stop(app->bad_usb_script);
|
||||
consumed = true;
|
||||
} else if(event.event == InputKeyRight) {
|
||||
bad_usb_script_pause_resume(app->bad_usb_script);
|
||||
if(bad_usb_view_is_idle_state(app->bad_usb_view)) {
|
||||
bad_usb_set_interface(
|
||||
app,
|
||||
app->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb :
|
||||
BadUsbHidInterfaceBle);
|
||||
bad_usb_script_close(app->bad_usb_script);
|
||||
app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);
|
||||
} else {
|
||||
bad_usb_script_pause_resume(app->bad_usb_script);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
|
@ -39,7 +52,9 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
|
|||
void bad_usb_scene_work_on_enter(void* context) {
|
||||
BadUsbApp* app = context;
|
||||
|
||||
app->bad_usb_script = bad_usb_script_open(app->file_path);
|
||||
bad_usb_view_set_interface(app->bad_usb_view, app->interface);
|
||||
|
||||
app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);
|
||||
bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
|
||||
|
||||
FuriString* file_name;
|
||||
|
|
|
@ -18,6 +18,7 @@ typedef struct {
|
|||
BadUsbState state;
|
||||
bool pause_wait;
|
||||
uint8_t anim_frame;
|
||||
BadUsbHidInterface interface;
|
||||
} BadUsbModel;
|
||||
|
||||
static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
|
@ -40,14 +41,24 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
|||
|
||||
furi_string_reset(disp_str);
|
||||
|
||||
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
|
||||
if(model->interface == BadUsbHidInterfaceBle) {
|
||||
canvas_draw_icon(canvas, 22, 24, &I_Bad_BLE_48x22);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
|
||||
}
|
||||
|
||||
BadUsbWorkerState state = model->state.state;
|
||||
|
||||
if((state == BadUsbStateIdle) || (state == BadUsbStateDone) ||
|
||||
(state == BadUsbStateNotConnected)) {
|
||||
elements_button_center(canvas, "Run");
|
||||
elements_button_left(canvas, "Layout");
|
||||
if(model->interface == BadUsbHidInterfaceBle) {
|
||||
elements_button_right(canvas, "USB");
|
||||
elements_button_left(canvas, "Config");
|
||||
} else {
|
||||
elements_button_right(canvas, "BLE");
|
||||
elements_button_left(canvas, "Layout");
|
||||
}
|
||||
} else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) {
|
||||
elements_button_center(canvas, "Stop");
|
||||
if(!model->pause_wait) {
|
||||
|
@ -266,6 +277,10 @@ void bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st) {
|
|||
true);
|
||||
}
|
||||
|
||||
void bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface) {
|
||||
with_view_model(bad_usb->view, BadUsbModel * model, { model->interface = interface; }, true);
|
||||
}
|
||||
|
||||
bool bad_usb_view_is_idle_state(BadUsb* bad_usb) {
|
||||
bool is_idle = false;
|
||||
with_view_model(
|
||||
|
|
|
@ -23,4 +23,6 @@ void bad_usb_view_set_layout(BadUsb* bad_usb, const char* layout);
|
|||
|
||||
void bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st);
|
||||
|
||||
void bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface);
|
||||
|
||||
bool bad_usb_view_is_idle_state(BadUsb* bad_usb);
|
||||
|
|
|
@ -4489,3 +4489,8 @@ EA19E58DD046
|
|||
3F41891454EE
|
||||
7EDAE7923287
|
||||
11DDA4862A1C
|
||||
|
||||
# H World Hotel Chain Room Keys
|
||||
543071543071
|
||||
5F01015F0101
|
||||
200510241234
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
App(
|
||||
appid="bad_ble",
|
||||
name="Bad BLE",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="bad_ble_app",
|
||||
stack_size=2 * 1024,
|
||||
icon="A_BadUsb_14",
|
||||
fap_libs=["assets", "ble_profile"],
|
||||
fap_icon="icon.png",
|
||||
fap_icon_assets="assets",
|
||||
fap_category="Bluetooth",
|
||||
)
|
|
@ -1,196 +0,0 @@
|
|||
#include "bad_ble_app_i.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <storage/storage.h>
|
||||
#include <lib/toolbox/path.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define BAD_BLE_SETTINGS_PATH BAD_BLE_APP_BASE_FOLDER "/.badble.settings"
|
||||
#define BAD_BLE_SETTINGS_FILE_TYPE "Flipper BadBLE Settings File"
|
||||
#define BAD_BLE_SETTINGS_VERSION 1
|
||||
#define BAD_BLE_SETTINGS_DEFAULT_LAYOUT BAD_BLE_APP_PATH_LAYOUT_FOLDER "/en-US.kl"
|
||||
|
||||
static bool bad_ble_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
BadBleApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool bad_ble_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BadBleApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void bad_ble_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BadBleApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void bad_ble_load_settings(BadBleApp* app) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff = flipper_format_file_alloc(storage);
|
||||
bool state = false;
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
uint32_t version = 0;
|
||||
|
||||
if(flipper_format_file_open_existing(fff, BAD_BLE_SETTINGS_PATH)) {
|
||||
do {
|
||||
if(!flipper_format_read_header(fff, temp_str, &version)) break;
|
||||
if((strcmp(furi_string_get_cstr(temp_str), BAD_BLE_SETTINGS_FILE_TYPE) != 0) ||
|
||||
(version != BAD_BLE_SETTINGS_VERSION))
|
||||
break;
|
||||
|
||||
if(!flipper_format_read_string(fff, "layout", temp_str)) break;
|
||||
|
||||
state = true;
|
||||
} while(0);
|
||||
}
|
||||
flipper_format_free(fff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
if(state) {
|
||||
furi_string_set(app->keyboard_layout, temp_str);
|
||||
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
FileInfo layout_file_info;
|
||||
FS_Error file_check_err = storage_common_stat(
|
||||
fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
if((file_check_err != FSE_OK) || (layout_file_info.size != 256)) {
|
||||
furi_string_set(app->keyboard_layout, BAD_BLE_SETTINGS_DEFAULT_LAYOUT);
|
||||
}
|
||||
} else {
|
||||
furi_string_set(app->keyboard_layout, BAD_BLE_SETTINGS_DEFAULT_LAYOUT);
|
||||
}
|
||||
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static void bad_ble_save_settings(BadBleApp* app) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff = flipper_format_file_alloc(storage);
|
||||
|
||||
if(flipper_format_file_open_always(fff, BAD_BLE_SETTINGS_PATH)) {
|
||||
do {
|
||||
if(!flipper_format_write_header_cstr(
|
||||
fff, BAD_BLE_SETTINGS_FILE_TYPE, BAD_BLE_SETTINGS_VERSION))
|
||||
break;
|
||||
if(!flipper_format_write_string(fff, "layout", app->keyboard_layout)) break;
|
||||
} while(0);
|
||||
}
|
||||
|
||||
flipper_format_free(fff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
BadBleApp* bad_ble_app_alloc(char* arg) {
|
||||
BadBleApp* app = malloc(sizeof(BadBleApp));
|
||||
|
||||
app->bad_ble_script = NULL;
|
||||
|
||||
app->file_path = furi_string_alloc();
|
||||
app->keyboard_layout = furi_string_alloc();
|
||||
if(arg && strlen(arg)) {
|
||||
furi_string_set(app->file_path, arg);
|
||||
}
|
||||
|
||||
bad_ble_load_settings(app);
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&bad_ble_scene_handlers, app);
|
||||
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, bad_ble_app_tick_event_callback, 500);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, bad_ble_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, bad_ble_app_back_event_callback);
|
||||
|
||||
// Custom Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BadBleAppViewWidget, widget_get_view(app->widget));
|
||||
|
||||
// Popup
|
||||
app->popup = popup_alloc();
|
||||
view_dispatcher_add_view(app->view_dispatcher, BadBleAppViewPopup, popup_get_view(app->popup));
|
||||
|
||||
app->var_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
BadBleAppViewConfig,
|
||||
variable_item_list_get_view(app->var_item_list));
|
||||
|
||||
app->bad_ble_view = bad_ble_view_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BadBleAppViewWork, bad_ble_view_get_view(app->bad_ble_view));
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
if(!furi_string_empty(app->file_path)) {
|
||||
scene_manager_next_scene(app->scene_manager, BadBleSceneWork);
|
||||
} else {
|
||||
furi_string_set(app->file_path, BAD_BLE_APP_BASE_FOLDER);
|
||||
scene_manager_next_scene(app->scene_manager, BadBleSceneFileSelect);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void bad_ble_app_free(BadBleApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
if(app->bad_ble_script) {
|
||||
bad_ble_script_close(app->bad_ble_script);
|
||||
app->bad_ble_script = NULL;
|
||||
}
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewWork);
|
||||
bad_ble_view_free(app->bad_ble_view);
|
||||
|
||||
// Custom Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
// Popup
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewPopup);
|
||||
popup_free(app->popup);
|
||||
|
||||
// Config menu
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewConfig);
|
||||
variable_item_list_free(app->var_item_list);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
bad_ble_save_settings(app);
|
||||
|
||||
furi_string_free(app->file_path);
|
||||
furi_string_free(app->keyboard_layout);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t bad_ble_app(void* p) {
|
||||
BadBleApp* bad_ble_app = bad_ble_app_alloc((char*)p);
|
||||
|
||||
view_dispatcher_run(bad_ble_app->view_dispatcher);
|
||||
|
||||
bad_ble_app_free(bad_ble_app);
|
||||
return 0;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct BadBleApp BadBleApp;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,53 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "bad_ble_app.h"
|
||||
#include "scenes/bad_ble_scene.h"
|
||||
#include "helpers/ducky_script.h"
|
||||
#include "helpers/bad_ble_hid.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include "views/bad_ble_view.h"
|
||||
|
||||
#define BAD_BLE_APP_BASE_FOLDER EXT_PATH("badusb")
|
||||
#define BAD_BLE_APP_PATH_LAYOUT_FOLDER BAD_BLE_APP_BASE_FOLDER "/assets/layouts"
|
||||
#define BAD_BLE_APP_SCRIPT_EXTENSION ".txt"
|
||||
#define BAD_BLE_APP_LAYOUT_EXTENSION ".kl"
|
||||
|
||||
typedef enum {
|
||||
BadBleAppErrorNoFiles,
|
||||
BadBleAppErrorCloseRpc,
|
||||
} BadBleAppError;
|
||||
|
||||
struct BadBleApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
DialogsApp* dialogs;
|
||||
Widget* widget;
|
||||
Popup* popup;
|
||||
VariableItemList* var_item_list;
|
||||
|
||||
BadBleAppError error;
|
||||
FuriString* file_path;
|
||||
FuriString* keyboard_layout;
|
||||
BadBle* bad_ble_view;
|
||||
BadBleScript* bad_ble_script;
|
||||
|
||||
BadBleHidInterface interface;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
BadBleAppViewWidget,
|
||||
BadBleAppViewPopup,
|
||||
BadBleAppViewWork,
|
||||
BadBleAppViewConfig,
|
||||
} BadBleAppView;
|
|
@ -1,157 +0,0 @@
|
|||
#include "bad_ble_hid.h"
|
||||
#include <extra_profiles/hid_profile.h>
|
||||
#include <bt/bt_service/bt.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "BadBLE HID"
|
||||
|
||||
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
|
||||
|
||||
typedef struct {
|
||||
Bt* bt;
|
||||
FuriHalBleProfileBase* profile;
|
||||
HidStateCallback state_callback;
|
||||
void* callback_context;
|
||||
bool is_connected;
|
||||
} BleHidInstance;
|
||||
|
||||
static const BleProfileHidParams ble_hid_params = {
|
||||
.device_name_prefix = "BadBLE",
|
||||
.mac_xor = 0x0002,
|
||||
};
|
||||
|
||||
static void hid_ble_connection_status_callback(BtStatus status, void* context) {
|
||||
furi_assert(context);
|
||||
BleHidInstance* ble_hid = context;
|
||||
ble_hid->is_connected = (status == BtStatusConnected);
|
||||
if(ble_hid->state_callback) {
|
||||
ble_hid->state_callback(ble_hid->is_connected, ble_hid->callback_context);
|
||||
}
|
||||
}
|
||||
|
||||
void* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) {
|
||||
UNUSED(hid_cfg);
|
||||
BleHidInstance* ble_hid = malloc(sizeof(BleHidInstance));
|
||||
ble_hid->bt = furi_record_open(RECORD_BT);
|
||||
bt_disconnect(ble_hid->bt);
|
||||
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
|
||||
bt_keys_storage_set_storage_path(ble_hid->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
|
||||
|
||||
ble_hid->profile = bt_profile_start(ble_hid->bt, ble_profile_hid, (void*)&ble_hid_params);
|
||||
furi_check(ble_hid->profile);
|
||||
|
||||
furi_hal_bt_start_advertising();
|
||||
|
||||
bt_set_status_changed_callback(ble_hid->bt, hid_ble_connection_status_callback, ble_hid);
|
||||
|
||||
return ble_hid;
|
||||
}
|
||||
|
||||
void hid_ble_deinit(void* inst) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
|
||||
bt_set_status_changed_callback(ble_hid->bt, NULL, NULL);
|
||||
bt_disconnect(ble_hid->bt);
|
||||
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
bt_keys_storage_set_default_path(ble_hid->bt);
|
||||
|
||||
furi_check(bt_profile_restore_default(ble_hid->bt));
|
||||
furi_record_close(RECORD_BT);
|
||||
free(ble_hid);
|
||||
}
|
||||
|
||||
void hid_ble_set_state_callback(void* inst, HidStateCallback cb, void* context) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
ble_hid->state_callback = cb;
|
||||
ble_hid->callback_context = context;
|
||||
}
|
||||
|
||||
bool hid_ble_is_connected(void* inst) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_hid->is_connected;
|
||||
}
|
||||
|
||||
bool hid_ble_kb_press(void* inst, uint16_t button) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_profile_hid_kb_press(ble_hid->profile, button);
|
||||
}
|
||||
|
||||
bool hid_ble_kb_release(void* inst, uint16_t button) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_profile_hid_kb_release(ble_hid->profile, button);
|
||||
}
|
||||
|
||||
bool hid_ble_consumer_press(void* inst, uint16_t button) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_profile_hid_consumer_key_press(ble_hid->profile, button);
|
||||
}
|
||||
|
||||
bool hid_ble_consumer_release(void* inst, uint16_t button) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
return ble_profile_hid_consumer_key_release(ble_hid->profile, button);
|
||||
}
|
||||
|
||||
bool hid_ble_release_all(void* inst) {
|
||||
BleHidInstance* ble_hid = inst;
|
||||
furi_assert(ble_hid);
|
||||
bool state = ble_profile_hid_kb_release_all(ble_hid->profile);
|
||||
state &= ble_profile_hid_consumer_key_release_all(ble_hid->profile);
|
||||
return state;
|
||||
}
|
||||
|
||||
uint8_t hid_ble_get_led_state(void* inst) {
|
||||
UNUSED(inst);
|
||||
FURI_LOG_W(TAG, "hid_ble_get_led_state not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const BadBleHidApi hid_api_ble = {
|
||||
.init = hid_ble_init,
|
||||
.deinit = hid_ble_deinit,
|
||||
.set_state_callback = hid_ble_set_state_callback,
|
||||
.is_connected = hid_ble_is_connected,
|
||||
|
||||
.kb_press = hid_ble_kb_press,
|
||||
.kb_release = hid_ble_kb_release,
|
||||
.consumer_press = hid_ble_consumer_press,
|
||||
.consumer_release = hid_ble_consumer_release,
|
||||
.release_all = hid_ble_release_all,
|
||||
.get_led_state = hid_ble_get_led_state,
|
||||
};
|
||||
|
||||
const BadBleHidApi* bad_ble_hid_get_interface(BadBleHidInterface interface) {
|
||||
UNUSED(interface);
|
||||
return &hid_api_ble;
|
||||
}
|
||||
|
||||
void bad_ble_hid_ble_remove_pairing(void) {
|
||||
Bt* bt = furi_record_open(RECORD_BT);
|
||||
bt_disconnect(bt);
|
||||
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
|
||||
furi_hal_bt_stop_advertising();
|
||||
|
||||
bt_keys_storage_set_storage_path(bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
|
||||
bt_forget_bonded_devices(bt);
|
||||
|
||||
// Wait 2nd core to update nvm storage
|
||||
furi_delay_ms(200);
|
||||
bt_keys_storage_set_default_path(bt);
|
||||
|
||||
furi_check(bt_profile_restore_default(bt));
|
||||
furi_record_close(RECORD_BT);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef enum {
|
||||
BadBleHidInterfaceBle,
|
||||
} BadBleHidInterface;
|
||||
|
||||
typedef struct {
|
||||
void* (*init)(FuriHalUsbHidConfig* hid_cfg);
|
||||
void (*deinit)(void* inst);
|
||||
void (*set_state_callback)(void* inst, HidStateCallback cb, void* context);
|
||||
bool (*is_connected)(void* inst);
|
||||
|
||||
bool (*kb_press)(void* inst, uint16_t button);
|
||||
bool (*kb_release)(void* inst, uint16_t button);
|
||||
bool (*consumer_press)(void* inst, uint16_t button);
|
||||
bool (*consumer_release)(void* inst, uint16_t button);
|
||||
bool (*release_all)(void* inst);
|
||||
uint8_t (*get_led_state)(void* inst);
|
||||
} BadBleHidApi;
|
||||
|
||||
const BadBleHidApi* bad_ble_hid_get_interface(BadBleHidInterface interface);
|
||||
|
||||
void bad_ble_hid_ble_remove_pairing(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,716 +0,0 @@
|
|||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/strint.h>
|
||||
#include <storage/storage.h>
|
||||
#include "ducky_script.h"
|
||||
#include "ducky_script_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "BadBle"
|
||||
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
#define BADUSB_ASCII_TO_KEY(script, x) \
|
||||
(((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
|
||||
|
||||
typedef enum {
|
||||
WorkerEvtStartStop = (1 << 0),
|
||||
WorkerEvtPauseResume = (1 << 1),
|
||||
WorkerEvtEnd = (1 << 2),
|
||||
WorkerEvtConnect = (1 << 3),
|
||||
WorkerEvtDisconnect = (1 << 4),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
static const char ducky_cmd_id[] = {"ID"};
|
||||
|
||||
static const uint8_t numpad_keys[10] = {
|
||||
HID_KEYPAD_0,
|
||||
HID_KEYPAD_1,
|
||||
HID_KEYPAD_2,
|
||||
HID_KEYPAD_3,
|
||||
HID_KEYPAD_4,
|
||||
HID_KEYPAD_5,
|
||||
HID_KEYPAD_6,
|
||||
HID_KEYPAD_7,
|
||||
HID_KEYPAD_8,
|
||||
HID_KEYPAD_9,
|
||||
};
|
||||
|
||||
uint32_t ducky_get_command_len(const char* line) {
|
||||
uint32_t len = strlen(line);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(line[i] == ' ') return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ducky_is_line_end(const char chr) {
|
||||
return (chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n');
|
||||
}
|
||||
|
||||
uint16_t ducky_get_keycode(BadBleScript* bad_ble, const char* param, bool accept_chars) {
|
||||
uint16_t keycode = ducky_get_keycode_by_name(param);
|
||||
if(keycode != HID_KEYBOARD_NONE) {
|
||||
return keycode;
|
||||
}
|
||||
|
||||
if((accept_chars) && (strlen(param) > 0)) {
|
||||
return BADUSB_ASCII_TO_KEY(bad_ble, param[0]) & 0xFF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ducky_get_number(const char* param, uint32_t* val) {
|
||||
uint32_t value = 0;
|
||||
if(strint_to_uint32(param, NULL, &value, 10) == StrintParseNoError) {
|
||||
*val = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ducky_numlock_on(BadBleScript* bad_ble) {
|
||||
if((bad_ble->hid->get_led_state(bad_ble->hid_inst) & HID_KB_LED_NUM) == 0) {
|
||||
bad_ble->hid->kb_press(bad_ble->hid_inst, HID_KEYBOARD_LOCK_NUM_LOCK);
|
||||
bad_ble->hid->kb_release(bad_ble->hid_inst, HID_KEYBOARD_LOCK_NUM_LOCK);
|
||||
}
|
||||
}
|
||||
|
||||
bool ducky_numpad_press(BadBleScript* bad_ble, const char num) {
|
||||
if((num < '0') || (num > '9')) return false;
|
||||
|
||||
uint16_t key = numpad_keys[num - '0'];
|
||||
bad_ble->hid->kb_press(bad_ble->hid_inst, key);
|
||||
bad_ble->hid->kb_release(bad_ble->hid_inst, key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ducky_altchar(BadBleScript* bad_ble, const char* charcode) {
|
||||
uint8_t i = 0;
|
||||
bool state = false;
|
||||
|
||||
bad_ble->hid->kb_press(bad_ble->hid_inst, KEY_MOD_LEFT_ALT);
|
||||
|
||||
while(!ducky_is_line_end(charcode[i])) {
|
||||
state = ducky_numpad_press(bad_ble, charcode[i]);
|
||||
if(state == false) break;
|
||||
i++;
|
||||
}
|
||||
|
||||
bad_ble->hid->kb_release(bad_ble->hid_inst, KEY_MOD_LEFT_ALT);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool ducky_altstring(BadBleScript* bad_ble, const char* param) {
|
||||
uint32_t i = 0;
|
||||
bool state = false;
|
||||
|
||||
while(param[i] != '\0') {
|
||||
if((param[i] < ' ') || (param[i] > '~')) {
|
||||
i++;
|
||||
continue; // Skip non-printable chars
|
||||
}
|
||||
|
||||
char temp_str[4];
|
||||
snprintf(temp_str, 4, "%u", param[i]);
|
||||
|
||||
state = ducky_altchar(bad_ble, temp_str);
|
||||
if(state == false) break;
|
||||
i++;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
int32_t ducky_error(BadBleScript* bad_ble, const char* text, ...) {
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
|
||||
vsnprintf(bad_ble->st.error, sizeof(bad_ble->st.error), text, args);
|
||||
|
||||
va_end(args);
|
||||
return SCRIPT_STATE_ERROR;
|
||||
}
|
||||
|
||||
bool ducky_string(BadBleScript* bad_ble, const char* param) {
|
||||
uint32_t i = 0;
|
||||
|
||||
while(param[i] != '\0') {
|
||||
if(param[i] != '\n') {
|
||||
uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_ble, param[i]);
|
||||
if(keycode != HID_KEYBOARD_NONE) {
|
||||
bad_ble->hid->kb_press(bad_ble->hid_inst, keycode);
|
||||
bad_ble->hid->kb_release(bad_ble->hid_inst, keycode);
|
||||
}
|
||||
} else {
|
||||
bad_ble->hid->kb_press(bad_ble->hid_inst, HID_KEYBOARD_RETURN);
|
||||
bad_ble->hid->kb_release(bad_ble->hid_inst, HID_KEYBOARD_RETURN);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
bad_ble->stringdelay = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ducky_string_next(BadBleScript* bad_ble) {
|
||||
if(bad_ble->string_print_pos >= furi_string_size(bad_ble->string_print)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char print_char = furi_string_get_char(bad_ble->string_print, bad_ble->string_print_pos);
|
||||
|
||||
if(print_char != '\n') {
|
||||
uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_ble, print_char);
|
||||
if(keycode != HID_KEYBOARD_NONE) {
|
||||
bad_ble->hid->kb_press(bad_ble->hid_inst, keycode);
|
||||
bad_ble->hid->kb_release(bad_ble->hid_inst, keycode);
|
||||
}
|
||||
} else {
|
||||
bad_ble->hid->kb_press(bad_ble->hid_inst, HID_KEYBOARD_RETURN);
|
||||
bad_ble->hid->kb_release(bad_ble->hid_inst, HID_KEYBOARD_RETURN);
|
||||
}
|
||||
|
||||
bad_ble->string_print_pos++;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int32_t ducky_parse_line(BadBleScript* bad_ble, FuriString* line) {
|
||||
uint32_t line_len = furi_string_size(line);
|
||||
const char* line_tmp = furi_string_get_cstr(line);
|
||||
|
||||
if(line_len == 0) {
|
||||
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
||||
}
|
||||
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
|
||||
|
||||
// Ducky Lang Functions
|
||||
int32_t cmd_result = ducky_execute_cmd(bad_ble, line_tmp);
|
||||
if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) {
|
||||
return cmd_result;
|
||||
}
|
||||
|
||||
// Special keys + modifiers
|
||||
uint16_t key = ducky_get_keycode(bad_ble, line_tmp, false);
|
||||
if(key == HID_KEYBOARD_NONE) {
|
||||
return ducky_error(bad_ble, "No keycode defined for %s", line_tmp);
|
||||
}
|
||||
if((key & 0xFF00) != 0) {
|
||||
// It's a modifier key
|
||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||
key |= ducky_get_keycode(bad_ble, line_tmp, true);
|
||||
}
|
||||
bad_ble->hid->kb_press(bad_ble->hid_inst, key);
|
||||
bad_ble->hid->kb_release(bad_ble->hid_inst, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ducky_set_usb_id(BadBleScript* bad_ble, const char* line) {
|
||||
if(sscanf(line, "%lX:%lX", &bad_ble->hid_cfg.vid, &bad_ble->hid_cfg.pid) == 2) {
|
||||
bad_ble->hid_cfg.manuf[0] = '\0';
|
||||
bad_ble->hid_cfg.product[0] = '\0';
|
||||
|
||||
uint8_t id_len = ducky_get_command_len(line);
|
||||
if(!ducky_is_line_end(line[id_len + 1])) {
|
||||
sscanf(
|
||||
&line[id_len + 1],
|
||||
"%31[^\r\n:]:%31[^\r\n]",
|
||||
bad_ble->hid_cfg.manuf,
|
||||
bad_ble->hid_cfg.product);
|
||||
}
|
||||
FURI_LOG_D(
|
||||
WORKER_TAG,
|
||||
"set id: %04lX:%04lX mfr:%s product:%s",
|
||||
bad_ble->hid_cfg.vid,
|
||||
bad_ble->hid_cfg.pid,
|
||||
bad_ble->hid_cfg.manuf,
|
||||
bad_ble->hid_cfg.product);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bad_ble_hid_state_callback(bool state, void* context) {
|
||||
furi_assert(context);
|
||||
BadBleScript* bad_ble = context;
|
||||
|
||||
if(state == true) {
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtConnect);
|
||||
} else {
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtDisconnect);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ducky_script_preload(BadBleScript* bad_ble, File* script_file) {
|
||||
uint8_t ret = 0;
|
||||
uint32_t line_len = 0;
|
||||
|
||||
furi_string_reset(bad_ble->line);
|
||||
|
||||
do {
|
||||
ret = storage_file_read(script_file, bad_ble->file_buf, FILE_BUFFER_LEN);
|
||||
for(uint16_t i = 0; i < ret; i++) {
|
||||
if(bad_ble->file_buf[i] == '\n' && line_len > 0) {
|
||||
bad_ble->st.line_nb++;
|
||||
line_len = 0;
|
||||
} else {
|
||||
if(bad_ble->st.line_nb == 0) { // Save first line
|
||||
furi_string_push_back(bad_ble->line, bad_ble->file_buf[i]);
|
||||
}
|
||||
line_len++;
|
||||
}
|
||||
}
|
||||
if(storage_file_eof(script_file)) {
|
||||
if(line_len > 0) {
|
||||
bad_ble->st.line_nb++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(ret > 0);
|
||||
|
||||
const char* line_tmp = furi_string_get_cstr(bad_ble->line);
|
||||
bool id_set = false; // Looking for ID command at first line
|
||||
if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
|
||||
id_set = ducky_set_usb_id(bad_ble, &line_tmp[strlen(ducky_cmd_id) + 1]);
|
||||
}
|
||||
|
||||
if(id_set) {
|
||||
bad_ble->hid_inst = bad_ble->hid->init(&bad_ble->hid_cfg);
|
||||
} else {
|
||||
bad_ble->hid_inst = bad_ble->hid->init(NULL);
|
||||
}
|
||||
bad_ble->hid->set_state_callback(bad_ble->hid_inst, bad_ble_hid_state_callback, bad_ble);
|
||||
|
||||
storage_file_seek(script_file, 0, true);
|
||||
furi_string_reset(bad_ble->line);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t ducky_script_execute_next(BadBleScript* bad_ble, File* script_file) {
|
||||
int32_t delay_val = 0;
|
||||
|
||||
if(bad_ble->repeat_cnt > 0) {
|
||||
bad_ble->repeat_cnt--;
|
||||
delay_val = ducky_parse_line(bad_ble, bad_ble->line_prev);
|
||||
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||
return 0;
|
||||
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
|
||||
return delay_val;
|
||||
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button
|
||||
return delay_val;
|
||||
} else if(delay_val < 0) { // Script error
|
||||
bad_ble->st.error_line = bad_ble->st.line_cur - 1;
|
||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_ble->st.line_cur - 1U);
|
||||
return SCRIPT_STATE_ERROR;
|
||||
} else {
|
||||
return delay_val + bad_ble->defdelay;
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_set(bad_ble->line_prev, bad_ble->line);
|
||||
furi_string_reset(bad_ble->line);
|
||||
|
||||
while(1) {
|
||||
if(bad_ble->buf_len == 0) {
|
||||
bad_ble->buf_len = storage_file_read(script_file, bad_ble->file_buf, FILE_BUFFER_LEN);
|
||||
if(storage_file_eof(script_file)) {
|
||||
if((bad_ble->buf_len < FILE_BUFFER_LEN) && (bad_ble->file_end == false)) {
|
||||
bad_ble->file_buf[bad_ble->buf_len] = '\n';
|
||||
bad_ble->buf_len++;
|
||||
bad_ble->file_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
bad_ble->buf_start = 0;
|
||||
if(bad_ble->buf_len == 0) return SCRIPT_STATE_END;
|
||||
}
|
||||
for(uint8_t i = bad_ble->buf_start; i < (bad_ble->buf_start + bad_ble->buf_len); i++) {
|
||||
if(bad_ble->file_buf[i] == '\n' && furi_string_size(bad_ble->line) > 0) {
|
||||
bad_ble->st.line_cur++;
|
||||
bad_ble->buf_len = bad_ble->buf_len + bad_ble->buf_start - (i + 1);
|
||||
bad_ble->buf_start = i + 1;
|
||||
furi_string_trim(bad_ble->line);
|
||||
delay_val = ducky_parse_line(bad_ble, bad_ble->line);
|
||||
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||
return 0;
|
||||
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
|
||||
return delay_val;
|
||||
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button
|
||||
return delay_val;
|
||||
} else if(delay_val < 0) {
|
||||
bad_ble->st.error_line = bad_ble->st.line_cur;
|
||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_ble->st.line_cur);
|
||||
return SCRIPT_STATE_ERROR;
|
||||
} else {
|
||||
return delay_val + bad_ble->defdelay;
|
||||
}
|
||||
} else {
|
||||
furi_string_push_back(bad_ble->line, bad_ble->file_buf[i]);
|
||||
}
|
||||
}
|
||||
bad_ble->buf_len = 0;
|
||||
if(bad_ble->file_end) return SCRIPT_STATE_END;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t bad_ble_flags_get(uint32_t flags_mask, uint32_t timeout) {
|
||||
uint32_t flags = furi_thread_flags_get();
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
if(flags == 0) {
|
||||
flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout);
|
||||
furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout));
|
||||
} else {
|
||||
uint32_t state = furi_thread_flags_clear(flags);
|
||||
furi_check((state & FuriFlagError) == 0);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static int32_t bad_ble_worker(void* context) {
|
||||
BadBleScript* bad_ble = context;
|
||||
|
||||
BadBleWorkerState worker_state = BadBleStateInit;
|
||||
BadBleWorkerState pause_state = BadBleStateRunning;
|
||||
int32_t delay_val = 0;
|
||||
|
||||
FURI_LOG_I(WORKER_TAG, "Init");
|
||||
File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||
bad_ble->line = furi_string_alloc();
|
||||
bad_ble->line_prev = furi_string_alloc();
|
||||
bad_ble->string_print = furi_string_alloc();
|
||||
|
||||
while(1) {
|
||||
if(worker_state == BadBleStateInit) { // State: initialization
|
||||
if(storage_file_open(
|
||||
script_file,
|
||||
furi_string_get_cstr(bad_ble->file_path),
|
||||
FSAM_READ,
|
||||
FSOM_OPEN_EXISTING)) {
|
||||
if((ducky_script_preload(bad_ble, script_file)) && (bad_ble->st.line_nb > 0)) {
|
||||
if(bad_ble->hid->is_connected(bad_ble->hid_inst)) {
|
||||
worker_state = BadBleStateIdle; // Ready to run
|
||||
} else {
|
||||
worker_state = BadBleStateNotConnected; // USB not connected
|
||||
}
|
||||
} else {
|
||||
worker_state = BadBleStateScriptError; // Script preload error
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(WORKER_TAG, "File open error");
|
||||
worker_state = BadBleStateFileError; // File open error
|
||||
}
|
||||
bad_ble->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadBleStateNotConnected) { // State: USB not connected
|
||||
uint32_t flags = bad_ble_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop,
|
||||
FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtConnect) {
|
||||
worker_state = BadBleStateIdle; // Ready to run
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadBleStateWillRun; // Will run when USB is connected
|
||||
}
|
||||
bad_ble->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadBleStateIdle) { // State: ready to start
|
||||
uint32_t flags = bad_ble_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtStartStop) { // Start executing script
|
||||
dolphin_deed(DolphinDeedBadUsbPlayScript);
|
||||
delay_val = 0;
|
||||
bad_ble->buf_len = 0;
|
||||
bad_ble->st.line_cur = 0;
|
||||
bad_ble->defdelay = 0;
|
||||
bad_ble->stringdelay = 0;
|
||||
bad_ble->defstringdelay = 0;
|
||||
bad_ble->repeat_cnt = 0;
|
||||
bad_ble->key_hold_nb = 0;
|
||||
bad_ble->file_end = false;
|
||||
storage_file_seek(script_file, 0, true);
|
||||
worker_state = BadBleStateRunning;
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBleStateNotConnected; // USB disconnected
|
||||
}
|
||||
bad_ble->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadBleStateWillRun) { // State: start on connection
|
||||
uint32_t flags = bad_ble_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever);
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtConnect) { // Start executing script
|
||||
dolphin_deed(DolphinDeedBadUsbPlayScript);
|
||||
delay_val = 0;
|
||||
bad_ble->buf_len = 0;
|
||||
bad_ble->st.line_cur = 0;
|
||||
bad_ble->defdelay = 0;
|
||||
bad_ble->stringdelay = 0;
|
||||
bad_ble->defstringdelay = 0;
|
||||
bad_ble->repeat_cnt = 0;
|
||||
bad_ble->file_end = false;
|
||||
storage_file_seek(script_file, 0, true);
|
||||
// extra time for PC to recognize Flipper as keyboard
|
||||
flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop,
|
||||
FuriFlagWaitAny | FuriFlagNoClear,
|
||||
1500);
|
||||
if(flags == (unsigned)FuriFlagErrorTimeout) {
|
||||
// If nothing happened - start script execution
|
||||
worker_state = BadBleStateRunning;
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadBleStateIdle;
|
||||
furi_thread_flags_clear(WorkerEvtStartStop);
|
||||
}
|
||||
} else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution
|
||||
worker_state = BadBleStateNotConnected;
|
||||
}
|
||||
bad_ble->st.state = worker_state;
|
||||
|
||||
} else if(worker_state == BadBleStateRunning) { // State: running
|
||||
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||
FuriFlagWaitAny,
|
||||
delay_cur);
|
||||
|
||||
delay_val -= delay_cur;
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadBleStateIdle; // Stop executing script
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBleStateNotConnected; // USB disconnected
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
} else if(flags & WorkerEvtPauseResume) {
|
||||
pause_state = BadBleStateRunning;
|
||||
worker_state = BadBleStatePaused; // Pause
|
||||
}
|
||||
bad_ble->st.state = worker_state;
|
||||
continue;
|
||||
} else if(
|
||||
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
||||
(flags == (unsigned)FuriFlagErrorResource)) {
|
||||
if(delay_val > 0) {
|
||||
bad_ble->st.delay_remain--;
|
||||
continue;
|
||||
}
|
||||
bad_ble->st.state = BadBleStateRunning;
|
||||
delay_val = ducky_script_execute_next(bad_ble, script_file);
|
||||
if(delay_val == SCRIPT_STATE_ERROR) { // Script error
|
||||
delay_val = 0;
|
||||
worker_state = BadBleStateScriptError;
|
||||
bad_ble->st.state = worker_state;
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
} else if(delay_val == SCRIPT_STATE_END) { // End of script
|
||||
delay_val = 0;
|
||||
worker_state = BadBleStateIdle;
|
||||
bad_ble->st.state = BadBleStateDone;
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
continue;
|
||||
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays
|
||||
delay_val = bad_ble->defdelay;
|
||||
bad_ble->string_print_pos = 0;
|
||||
worker_state = BadBleStateStringDelay;
|
||||
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input
|
||||
worker_state = BadBleStateWaitForBtn;
|
||||
bad_ble->st.state = BadBleStateWaitForBtn; // Show long delays
|
||||
} else if(delay_val > 1000) {
|
||||
bad_ble->st.state = BadBleStateDelay; // Show long delays
|
||||
bad_ble->st.delay_remain = delay_val / 1000;
|
||||
}
|
||||
} else {
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
}
|
||||
} else if(worker_state == BadBleStateWaitForBtn) { // State: Wait for button Press
|
||||
uint32_t flags = bad_ble_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||
FuriWaitForever);
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
delay_val = 0;
|
||||
worker_state = BadBleStateRunning;
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBleStateNotConnected; // USB disconnected
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
}
|
||||
bad_ble->st.state = worker_state;
|
||||
continue;
|
||||
}
|
||||
} else if(worker_state == BadBleStatePaused) { // State: Paused
|
||||
uint32_t flags = bad_ble_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||
FuriWaitForever);
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadBleStateIdle; // Stop executing script
|
||||
bad_ble->st.state = worker_state;
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBleStateNotConnected; // USB disconnected
|
||||
bad_ble->st.state = worker_state;
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
} else if(flags & WorkerEvtPauseResume) {
|
||||
if(pause_state == BadBleStateRunning) {
|
||||
if(delay_val > 0) {
|
||||
bad_ble->st.state = BadBleStateDelay;
|
||||
bad_ble->st.delay_remain = delay_val / 1000;
|
||||
} else {
|
||||
bad_ble->st.state = BadBleStateRunning;
|
||||
delay_val = 0;
|
||||
}
|
||||
worker_state = BadBleStateRunning; // Resume
|
||||
} else if(pause_state == BadBleStateStringDelay) {
|
||||
bad_ble->st.state = BadBleStateRunning;
|
||||
worker_state = BadBleStateStringDelay; // Resume
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if(worker_state == BadBleStateStringDelay) { // State: print string with delays
|
||||
uint32_t delay = (bad_ble->stringdelay == 0) ? bad_ble->defstringdelay :
|
||||
bad_ble->stringdelay;
|
||||
uint32_t flags = bad_ble_flags_get(
|
||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||
delay);
|
||||
|
||||
if(!(flags & FuriFlagError)) {
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtStartStop) {
|
||||
worker_state = BadBleStateIdle; // Stop executing script
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
} else if(flags & WorkerEvtDisconnect) {
|
||||
worker_state = BadBleStateNotConnected; // USB disconnected
|
||||
bad_ble->hid->release_all(bad_ble->hid_inst);
|
||||
} else if(flags & WorkerEvtPauseResume) {
|
||||
pause_state = BadBleStateStringDelay;
|
||||
worker_state = BadBleStatePaused; // Pause
|
||||
}
|
||||
bad_ble->st.state = worker_state;
|
||||
continue;
|
||||
} else if(
|
||||
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
||||
(flags == (unsigned)FuriFlagErrorResource)) {
|
||||
bool string_end = ducky_string_next(bad_ble);
|
||||
if(string_end) {
|
||||
bad_ble->stringdelay = 0;
|
||||
worker_state = BadBleStateRunning;
|
||||
}
|
||||
} else {
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
}
|
||||
} else if(
|
||||
(worker_state == BadBleStateFileError) ||
|
||||
(worker_state == BadBleStateScriptError)) { // State: error
|
||||
uint32_t flags =
|
||||
bad_ble_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command
|
||||
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bad_ble->hid->set_state_callback(bad_ble->hid_inst, NULL, NULL);
|
||||
bad_ble->hid->deinit(bad_ble->hid_inst);
|
||||
|
||||
storage_file_close(script_file);
|
||||
storage_file_free(script_file);
|
||||
furi_string_free(bad_ble->line);
|
||||
furi_string_free(bad_ble->line_prev);
|
||||
furi_string_free(bad_ble->string_print);
|
||||
|
||||
FURI_LOG_I(WORKER_TAG, "End");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bad_ble_script_set_default_keyboard_layout(BadBleScript* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
memset(bad_ble->layout, HID_KEYBOARD_NONE, sizeof(bad_ble->layout));
|
||||
memcpy(bad_ble->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_ble->layout)));
|
||||
}
|
||||
|
||||
BadBleScript* bad_ble_script_open(FuriString* file_path, BadBleHidInterface interface) {
|
||||
furi_assert(file_path);
|
||||
|
||||
BadBleScript* bad_ble = malloc(sizeof(BadBleScript));
|
||||
bad_ble->file_path = furi_string_alloc();
|
||||
furi_string_set(bad_ble->file_path, file_path);
|
||||
bad_ble_script_set_default_keyboard_layout(bad_ble);
|
||||
|
||||
bad_ble->st.state = BadBleStateInit;
|
||||
bad_ble->st.error[0] = '\0';
|
||||
bad_ble->hid = bad_ble_hid_get_interface(interface);
|
||||
|
||||
bad_ble->thread = furi_thread_alloc_ex("BadBleWorker", 2048, bad_ble_worker, bad_ble);
|
||||
furi_thread_start(bad_ble->thread);
|
||||
return bad_ble;
|
||||
} //-V773
|
||||
|
||||
void bad_ble_script_close(BadBleScript* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtEnd);
|
||||
furi_thread_join(bad_ble->thread);
|
||||
furi_thread_free(bad_ble->thread);
|
||||
furi_string_free(bad_ble->file_path);
|
||||
free(bad_ble);
|
||||
}
|
||||
|
||||
void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path) {
|
||||
furi_assert(bad_ble);
|
||||
|
||||
if((bad_ble->st.state == BadBleStateRunning) || (bad_ble->st.state == BadBleStateDelay)) {
|
||||
// do not update keyboard layout while a script is running
|
||||
return;
|
||||
}
|
||||
|
||||
File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||
if(!furi_string_empty(layout_path)) { //-V1051
|
||||
if(storage_file_open(
|
||||
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
uint16_t layout[128];
|
||||
if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) {
|
||||
memcpy(bad_ble->layout, layout, sizeof(layout));
|
||||
}
|
||||
}
|
||||
storage_file_close(layout_file);
|
||||
} else {
|
||||
bad_ble_script_set_default_keyboard_layout(bad_ble);
|
||||
}
|
||||
storage_file_free(layout_file);
|
||||
}
|
||||
|
||||
void bad_ble_script_start_stop(BadBleScript* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtStartStop);
|
||||
}
|
||||
|
||||
void bad_ble_script_pause_resume(BadBleScript* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtPauseResume);
|
||||
}
|
||||
|
||||
BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
return &(bad_ble->st);
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "bad_ble_hid.h"
|
||||
|
||||
typedef enum {
|
||||
BadBleStateInit,
|
||||
BadBleStateNotConnected,
|
||||
BadBleStateIdle,
|
||||
BadBleStateWillRun,
|
||||
BadBleStateRunning,
|
||||
BadBleStateDelay,
|
||||
BadBleStateStringDelay,
|
||||
BadBleStateWaitForBtn,
|
||||
BadBleStatePaused,
|
||||
BadBleStateDone,
|
||||
BadBleStateScriptError,
|
||||
BadBleStateFileError,
|
||||
} BadBleWorkerState;
|
||||
|
||||
typedef struct {
|
||||
BadBleWorkerState state;
|
||||
size_t line_cur;
|
||||
size_t line_nb;
|
||||
uint32_t delay_remain;
|
||||
size_t error_line;
|
||||
char error[64];
|
||||
} BadBleState;
|
||||
|
||||
typedef struct BadBleScript BadBleScript;
|
||||
|
||||
BadBleScript* bad_ble_script_open(FuriString* file_path, BadBleHidInterface interface);
|
||||
|
||||
void bad_ble_script_close(BadBleScript* bad_ble);
|
||||
|
||||
void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path);
|
||||
|
||||
void bad_ble_script_start(BadBleScript* bad_ble);
|
||||
|
||||
void bad_ble_script_stop(BadBleScript* bad_ble);
|
||||
|
||||
void bad_ble_script_start_stop(BadBleScript* bad_ble);
|
||||
|
||||
void bad_ble_script_pause_resume(BadBleScript* bad_ble);
|
||||
|
||||
BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,241 +0,0 @@
|
|||
#include <furi_hal.h>
|
||||
#include "ducky_script.h"
|
||||
#include "ducky_script_i.h"
|
||||
|
||||
typedef int32_t (*DuckyCmdCallback)(BadBleScript* bad_usb, const char* line, int32_t param);
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
DuckyCmdCallback callback;
|
||||
int32_t param;
|
||||
} DuckyCmd;
|
||||
|
||||
static int32_t ducky_fnc_delay(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint32_t delay_val = 0;
|
||||
bool state = ducky_get_number(line, &delay_val);
|
||||
if((state) && (delay_val > 0)) {
|
||||
return (int32_t)delay_val;
|
||||
}
|
||||
|
||||
return ducky_error(bad_usb, "Invalid number %s", line);
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_defdelay(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
bool state = ducky_get_number(line, &bad_usb->defdelay);
|
||||
if(!state) {
|
||||
return ducky_error(bad_usb, "Invalid number %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_strdelay(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
bool state = ducky_get_number(line, &bad_usb->stringdelay);
|
||||
if(!state) {
|
||||
return ducky_error(bad_usb, "Invalid number %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_defstrdelay(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
bool state = ducky_get_number(line, &bad_usb->defstringdelay);
|
||||
if(!state) {
|
||||
return ducky_error(bad_usb, "Invalid number %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_string(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
furi_string_set_str(bad_usb->string_print, line);
|
||||
if(param == 1) {
|
||||
furi_string_cat(bad_usb->string_print, "\n");
|
||||
}
|
||||
|
||||
if(bad_usb->stringdelay == 0 &&
|
||||
bad_usb->defstringdelay == 0) { // stringdelay not set - run command immediately
|
||||
bool state = ducky_string(bad_usb, furi_string_get_cstr(bad_usb->string_print));
|
||||
if(!state) {
|
||||
return ducky_error(bad_usb, "Invalid string %s", line);
|
||||
}
|
||||
} else { // stringdelay is set - run command in thread to keep handling external events
|
||||
return SCRIPT_STATE_STRING_START;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_repeat(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
bool state = ducky_get_number(line, &bad_usb->repeat_cnt);
|
||||
if((!state) || (bad_usb->repeat_cnt == 0)) {
|
||||
return ducky_error(bad_usb, "Invalid number %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_sysrq(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint16_t key = ducky_get_keycode(bad_usb, line, true);
|
||||
bad_usb->hid->kb_press(bad_usb->hid_inst, KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
|
||||
bad_usb->hid->kb_press(bad_usb->hid_inst, key);
|
||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_altchar(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
ducky_numlock_on(bad_usb);
|
||||
bool state = ducky_altchar(bad_usb, line);
|
||||
if(!state) {
|
||||
return ducky_error(bad_usb, "Invalid altchar %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_altstring(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
ducky_numlock_on(bad_usb);
|
||||
bool state = ducky_altstring(bad_usb, line);
|
||||
if(!state) {
|
||||
return ducky_error(bad_usb, "Invalid altstring %s", line);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_hold(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint16_t key = ducky_get_keycode(bad_usb, line, true);
|
||||
if(key == HID_KEYBOARD_NONE) {
|
||||
return ducky_error(bad_usb, "No keycode defined for %s", line);
|
||||
}
|
||||
bad_usb->key_hold_nb++;
|
||||
if(bad_usb->key_hold_nb > (HID_KB_MAX_KEYS - 1)) {
|
||||
return ducky_error(bad_usb, "Too many keys are hold");
|
||||
}
|
||||
bad_usb->hid->kb_press(bad_usb->hid_inst, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_release(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint16_t key = ducky_get_keycode(bad_usb, line, true);
|
||||
if(key == HID_KEYBOARD_NONE) {
|
||||
return ducky_error(bad_usb, "No keycode defined for %s", line);
|
||||
}
|
||||
if(bad_usb->key_hold_nb == 0) {
|
||||
return ducky_error(bad_usb, "No keys are hold");
|
||||
}
|
||||
bad_usb->key_hold_nb--;
|
||||
bad_usb->hid->kb_release(bad_usb->hid_inst, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_media(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint16_t key = ducky_get_media_keycode_by_name(line);
|
||||
if(key == HID_CONSUMER_UNASSIGNED) {
|
||||
return ducky_error(bad_usb, "No keycode defined for %s", line);
|
||||
}
|
||||
bad_usb->hid->consumer_press(bad_usb->hid_inst, key);
|
||||
bad_usb->hid->consumer_release(bad_usb->hid_inst, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_globe(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
|
||||
line = &line[ducky_get_command_len(line) + 1];
|
||||
uint16_t key = ducky_get_keycode(bad_usb, line, true);
|
||||
if(key == HID_KEYBOARD_NONE) {
|
||||
return ducky_error(bad_usb, "No keycode defined for %s", line);
|
||||
}
|
||||
|
||||
bad_usb->hid->consumer_press(bad_usb->hid_inst, HID_CONSUMER_FN_GLOBE);
|
||||
bad_usb->hid->kb_press(bad_usb->hid_inst, key);
|
||||
bad_usb->hid->kb_release(bad_usb->hid_inst, key);
|
||||
bad_usb->hid->consumer_release(bad_usb->hid_inst, HID_CONSUMER_FN_GLOBE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_fnc_waitforbutton(BadBleScript* bad_usb, const char* line, int32_t param) {
|
||||
UNUSED(param);
|
||||
UNUSED(bad_usb);
|
||||
UNUSED(line);
|
||||
|
||||
return SCRIPT_STATE_WAIT_FOR_BTN;
|
||||
}
|
||||
|
||||
static const DuckyCmd ducky_commands[] = {
|
||||
{"REM", NULL, -1},
|
||||
{"ID", NULL, -1},
|
||||
{"DELAY", ducky_fnc_delay, -1},
|
||||
{"STRING", ducky_fnc_string, 0},
|
||||
{"STRINGLN", ducky_fnc_string, 1},
|
||||
{"DEFAULT_DELAY", ducky_fnc_defdelay, -1},
|
||||
{"DEFAULTDELAY", ducky_fnc_defdelay, -1},
|
||||
{"STRINGDELAY", ducky_fnc_strdelay, -1},
|
||||
{"STRING_DELAY", ducky_fnc_strdelay, -1},
|
||||
{"DEFAULT_STRING_DELAY", ducky_fnc_defstrdelay, -1},
|
||||
{"DEFAULTSTRINGDELAY", ducky_fnc_defstrdelay, -1},
|
||||
{"REPEAT", ducky_fnc_repeat, -1},
|
||||
{"SYSRQ", ducky_fnc_sysrq, -1},
|
||||
{"ALTCHAR", ducky_fnc_altchar, -1},
|
||||
{"ALTSTRING", ducky_fnc_altstring, -1},
|
||||
{"ALTCODE", ducky_fnc_altstring, -1},
|
||||
{"HOLD", ducky_fnc_hold, -1},
|
||||
{"RELEASE", ducky_fnc_release, -1},
|
||||
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
|
||||
{"MEDIA", ducky_fnc_media, -1},
|
||||
{"GLOBE", ducky_fnc_globe, -1},
|
||||
};
|
||||
|
||||
#define TAG "BadBle"
|
||||
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
int32_t ducky_execute_cmd(BadBleScript* bad_usb, const char* line) {
|
||||
size_t cmd_word_len = strcspn(line, " ");
|
||||
for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) {
|
||||
size_t cmd_compare_len = strlen(ducky_commands[i].name);
|
||||
|
||||
if(cmd_compare_len != cmd_word_len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) {
|
||||
if(ducky_commands[i].callback == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
return (ducky_commands[i].callback)(bad_usb, line, ducky_commands[i].param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SCRIPT_STATE_CMD_UNKNOWN;
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "ducky_script.h"
|
||||
#include "bad_ble_hid.h"
|
||||
|
||||
#define SCRIPT_STATE_ERROR (-1)
|
||||
#define SCRIPT_STATE_END (-2)
|
||||
#define SCRIPT_STATE_NEXT_LINE (-3)
|
||||
#define SCRIPT_STATE_CMD_UNKNOWN (-4)
|
||||
#define SCRIPT_STATE_STRING_START (-5)
|
||||
#define SCRIPT_STATE_WAIT_FOR_BTN (-6)
|
||||
|
||||
#define FILE_BUFFER_LEN 16
|
||||
|
||||
struct BadBleScript {
|
||||
FuriHalUsbHidConfig hid_cfg;
|
||||
const BadBleHidApi* hid;
|
||||
void* hid_inst;
|
||||
FuriThread* thread;
|
||||
BadBleState st;
|
||||
|
||||
FuriString* file_path;
|
||||
uint8_t file_buf[FILE_BUFFER_LEN + 1];
|
||||
uint8_t buf_start;
|
||||
uint8_t buf_len;
|
||||
bool file_end;
|
||||
|
||||
uint32_t defdelay;
|
||||
uint32_t stringdelay;
|
||||
uint32_t defstringdelay;
|
||||
uint16_t layout[128];
|
||||
|
||||
FuriString* line;
|
||||
FuriString* line_prev;
|
||||
uint32_t repeat_cnt;
|
||||
uint8_t key_hold_nb;
|
||||
|
||||
FuriString* string_print;
|
||||
size_t string_print_pos;
|
||||
};
|
||||
|
||||
uint16_t ducky_get_keycode(BadBleScript* bad_usb, const char* param, bool accept_chars);
|
||||
|
||||
uint32_t ducky_get_command_len(const char* line);
|
||||
|
||||
bool ducky_is_line_end(const char chr);
|
||||
|
||||
uint16_t ducky_get_keycode_by_name(const char* param);
|
||||
|
||||
uint16_t ducky_get_media_keycode_by_name(const char* param);
|
||||
|
||||
bool ducky_get_number(const char* param, uint32_t* val);
|
||||
|
||||
void ducky_numlock_on(BadBleScript* bad_usb);
|
||||
|
||||
bool ducky_numpad_press(BadBleScript* bad_usb, const char num);
|
||||
|
||||
bool ducky_altchar(BadBleScript* bad_usb, const char* charcode);
|
||||
|
||||
bool ducky_altstring(BadBleScript* bad_usb, const char* param);
|
||||
|
||||
bool ducky_string(BadBleScript* bad_usb, const char* param);
|
||||
|
||||
int32_t ducky_execute_cmd(BadBleScript* bad_usb, const char* line);
|
||||
|
||||
int32_t ducky_error(BadBleScript* bad_usb, const char* text, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,133 +0,0 @@
|
|||
#include <furi_hal.h>
|
||||
#include "ducky_script_i.h"
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
uint16_t keycode;
|
||||
} DuckyKey;
|
||||
|
||||
static const DuckyKey ducky_keys[] = {
|
||||
{"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT},
|
||||
{"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT},
|
||||
{"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
|
||||
{"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
|
||||
{"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
|
||||
{"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL},
|
||||
|
||||
{"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", HID_KEYBOARD_DOWN_ARROW},
|
||||
{"DOWN", HID_KEYBOARD_DOWN_ARROW},
|
||||
{"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
|
||||
{"LEFT", HID_KEYBOARD_LEFT_ARROW},
|
||||
{"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW},
|
||||
{"RIGHT", HID_KEYBOARD_RIGHT_ARROW},
|
||||
{"UPARROW", HID_KEYBOARD_UP_ARROW},
|
||||
{"UP", HID_KEYBOARD_UP_ARROW},
|
||||
|
||||
{"ENTER", HID_KEYBOARD_RETURN},
|
||||
{"BREAK", HID_KEYBOARD_PAUSE},
|
||||
{"PAUSE", HID_KEYBOARD_PAUSE},
|
||||
{"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
|
||||
{"DELETE", HID_KEYBOARD_DELETE_FORWARD},
|
||||
{"BACKSPACE", HID_KEYBOARD_DELETE},
|
||||
{"END", HID_KEYBOARD_END},
|
||||
{"ESC", HID_KEYBOARD_ESCAPE},
|
||||
{"ESCAPE", HID_KEYBOARD_ESCAPE},
|
||||
{"HOME", HID_KEYBOARD_HOME},
|
||||
{"INSERT", HID_KEYBOARD_INSERT},
|
||||
{"NUMLOCK", HID_KEYPAD_NUMLOCK},
|
||||
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
|
||||
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
|
||||
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
|
||||
{"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
|
||||
{"SPACE", HID_KEYBOARD_SPACEBAR},
|
||||
{"TAB", HID_KEYBOARD_TAB},
|
||||
{"MENU", HID_KEYBOARD_APPLICATION},
|
||||
{"APP", HID_KEYBOARD_APPLICATION},
|
||||
|
||||
{"F1", HID_KEYBOARD_F1},
|
||||
{"F2", HID_KEYBOARD_F2},
|
||||
{"F3", HID_KEYBOARD_F3},
|
||||
{"F4", HID_KEYBOARD_F4},
|
||||
{"F5", HID_KEYBOARD_F5},
|
||||
{"F6", HID_KEYBOARD_F6},
|
||||
{"F7", HID_KEYBOARD_F7},
|
||||
{"F8", HID_KEYBOARD_F8},
|
||||
{"F9", HID_KEYBOARD_F9},
|
||||
{"F10", HID_KEYBOARD_F10},
|
||||
{"F11", HID_KEYBOARD_F11},
|
||||
{"F12", HID_KEYBOARD_F12},
|
||||
{"F13", HID_KEYBOARD_F13},
|
||||
{"F14", HID_KEYBOARD_F14},
|
||||
{"F15", HID_KEYBOARD_F15},
|
||||
{"F16", HID_KEYBOARD_F16},
|
||||
{"F17", HID_KEYBOARD_F17},
|
||||
{"F18", HID_KEYBOARD_F18},
|
||||
{"F19", HID_KEYBOARD_F19},
|
||||
{"F20", HID_KEYBOARD_F20},
|
||||
{"F21", HID_KEYBOARD_F21},
|
||||
{"F22", HID_KEYBOARD_F22},
|
||||
{"F23", HID_KEYBOARD_F23},
|
||||
{"F24", HID_KEYBOARD_F24},
|
||||
};
|
||||
|
||||
static const DuckyKey ducky_media_keys[] = {
|
||||
{"POWER", HID_CONSUMER_POWER},
|
||||
{"REBOOT", HID_CONSUMER_RESET},
|
||||
{"SLEEP", HID_CONSUMER_SLEEP},
|
||||
{"LOGOFF", HID_CONSUMER_AL_LOGOFF},
|
||||
|
||||
{"EXIT", HID_CONSUMER_AC_EXIT},
|
||||
{"HOME", HID_CONSUMER_AC_HOME},
|
||||
{"BACK", HID_CONSUMER_AC_BACK},
|
||||
{"FORWARD", HID_CONSUMER_AC_FORWARD},
|
||||
{"REFRESH", HID_CONSUMER_AC_REFRESH},
|
||||
|
||||
{"SNAPSHOT", HID_CONSUMER_SNAPSHOT},
|
||||
|
||||
{"PLAY", HID_CONSUMER_PLAY},
|
||||
{"PAUSE", HID_CONSUMER_PAUSE},
|
||||
{"PLAY_PAUSE", HID_CONSUMER_PLAY_PAUSE},
|
||||
{"NEXT_TRACK", HID_CONSUMER_SCAN_NEXT_TRACK},
|
||||
{"PREV_TRACK", HID_CONSUMER_SCAN_PREVIOUS_TRACK},
|
||||
{"STOP", HID_CONSUMER_STOP},
|
||||
{"EJECT", HID_CONSUMER_EJECT},
|
||||
|
||||
{"MUTE", HID_CONSUMER_MUTE},
|
||||
{"VOLUME_UP", HID_CONSUMER_VOLUME_INCREMENT},
|
||||
{"VOLUME_DOWN", HID_CONSUMER_VOLUME_DECREMENT},
|
||||
|
||||
{"FN", HID_CONSUMER_FN_GLOBE},
|
||||
{"BRIGHT_UP", HID_CONSUMER_BRIGHTNESS_INCREMENT},
|
||||
{"BRIGHT_DOWN", HID_CONSUMER_BRIGHTNESS_DECREMENT},
|
||||
};
|
||||
|
||||
uint16_t ducky_get_keycode_by_name(const char* param) {
|
||||
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
|
||||
size_t key_cmd_len = strlen(ducky_keys[i].name);
|
||||
if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
|
||||
(ducky_is_line_end(param[key_cmd_len]))) {
|
||||
return ducky_keys[i].keycode;
|
||||
}
|
||||
}
|
||||
|
||||
return HID_KEYBOARD_NONE;
|
||||
}
|
||||
|
||||
uint16_t ducky_get_media_keycode_by_name(const char* param) {
|
||||
for(size_t i = 0; i < COUNT_OF(ducky_media_keys); i++) {
|
||||
size_t key_cmd_len = strlen(ducky_media_keys[i].name);
|
||||
if((strncmp(param, ducky_media_keys[i].name, key_cmd_len) == 0) &&
|
||||
(ducky_is_line_end(param[key_cmd_len]))) {
|
||||
return ducky_media_keys[i].keycode;
|
||||
}
|
||||
}
|
||||
|
||||
return HID_CONSUMER_UNASSIGNED;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 96 B |
|
@ -1,30 +0,0 @@
|
|||
#include "bad_ble_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const bad_ble_scene_on_enter_handlers[])(void*) = {
|
||||
#include "bad_ble_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_ble_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "bad_ble_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_ble_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "bad_ble_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers bad_ble_scene_handlers = {
|
||||
.on_enter_handlers = bad_ble_scene_on_enter_handlers,
|
||||
.on_event_handlers = bad_ble_scene_on_event_handlers,
|
||||
.on_exit_handlers = bad_ble_scene_on_exit_handlers,
|
||||
.scene_num = BadBleSceneNum,
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) BadBleScene##id,
|
||||
typedef enum {
|
||||
#include "bad_ble_scene_config.h"
|
||||
BadBleSceneNum,
|
||||
} BadBleScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers bad_ble_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "bad_ble_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_ble_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_ble_scene_config.h"
|
||||
#undef ADD_SCENE
|
|
@ -1,59 +0,0 @@
|
|||
#include "../bad_ble_app_i.h"
|
||||
|
||||
enum SubmenuIndex {
|
||||
ConfigIndexKeyboardLayout,
|
||||
ConfigIndexBleUnpair,
|
||||
};
|
||||
|
||||
void bad_ble_scene_config_select_callback(void* context, uint32_t index) {
|
||||
BadBleApp* bad_ble = context;
|
||||
|
||||
view_dispatcher_send_custom_event(bad_ble->view_dispatcher, index);
|
||||
}
|
||||
|
||||
static void draw_menu(BadBleApp* bad_ble) {
|
||||
VariableItemList* var_item_list = bad_ble->var_item_list;
|
||||
|
||||
variable_item_list_reset(var_item_list);
|
||||
|
||||
variable_item_list_add(var_item_list, "Keyboard Layout (Global)", 0, NULL, NULL);
|
||||
|
||||
variable_item_list_add(var_item_list, "Unpair Device", 0, NULL, NULL);
|
||||
}
|
||||
|
||||
void bad_ble_scene_config_on_enter(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
VariableItemList* var_item_list = bad_ble->var_item_list;
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
var_item_list, bad_ble_scene_config_select_callback, bad_ble);
|
||||
draw_menu(bad_ble);
|
||||
variable_item_list_set_selected_item(var_item_list, 0);
|
||||
|
||||
view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfig);
|
||||
}
|
||||
|
||||
bool bad_ble_scene_config_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBleApp* bad_ble = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == ConfigIndexKeyboardLayout) {
|
||||
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigLayout);
|
||||
} else if(event.event == ConfigIndexBleUnpair) {
|
||||
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfirmUnpair);
|
||||
} else {
|
||||
furi_crash("Unknown key type");
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_ble_scene_config_on_exit(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
VariableItemList* var_item_list = bad_ble->var_item_list;
|
||||
|
||||
variable_item_list_reset(var_item_list);
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
ADD_SCENE(bad_ble, file_select, FileSelect)
|
||||
ADD_SCENE(bad_ble, work, Work)
|
||||
ADD_SCENE(bad_ble, error, Error)
|
||||
ADD_SCENE(bad_ble, config, Config)
|
||||
ADD_SCENE(bad_ble, config_layout, ConfigLayout)
|
||||
ADD_SCENE(bad_ble, confirm_unpair, ConfirmUnpair)
|
||||
ADD_SCENE(bad_ble, unpair_done, UnpairDone)
|
|
@ -1,49 +0,0 @@
|
|||
#include "../bad_ble_app_i.h"
|
||||
#include <storage/storage.h>
|
||||
|
||||
static bool bad_ble_layout_select(BadBleApp* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
|
||||
FuriString* predefined_path;
|
||||
predefined_path = furi_string_alloc();
|
||||
if(!furi_string_empty(bad_ble->keyboard_layout)) {
|
||||
furi_string_set(predefined_path, bad_ble->keyboard_layout);
|
||||
} else {
|
||||
furi_string_set(predefined_path, BAD_BLE_APP_PATH_LAYOUT_FOLDER);
|
||||
}
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, BAD_BLE_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
|
||||
browser_options.base_path = BAD_BLE_APP_PATH_LAYOUT_FOLDER;
|
||||
browser_options.skip_assets = false;
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
bool res = dialog_file_browser_show(
|
||||
bad_ble->dialogs, bad_ble->keyboard_layout, predefined_path, &browser_options);
|
||||
|
||||
furi_string_free(predefined_path);
|
||||
return res;
|
||||
}
|
||||
|
||||
void bad_ble_scene_config_layout_on_enter(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
|
||||
if(bad_ble_layout_select(bad_ble)) {
|
||||
scene_manager_search_and_switch_to_previous_scene(bad_ble->scene_manager, BadBleSceneWork);
|
||||
} else {
|
||||
scene_manager_previous_scene(bad_ble->scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
bool bad_ble_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
// BadBleApp* bad_ble = context;
|
||||
return false;
|
||||
}
|
||||
|
||||
void bad_ble_scene_config_layout_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
// BadBleApp* bad_ble = context;
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#include "../bad_ble_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
BadBleCustomEventErrorBack,
|
||||
} BadBleCustomEvent;
|
||||
|
||||
static void
|
||||
bad_ble_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
BadBleApp* app = context;
|
||||
|
||||
if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, BadBleCustomEventErrorBack);
|
||||
}
|
||||
}
|
||||
|
||||
void bad_ble_scene_error_on_enter(void* context) {
|
||||
BadBleApp* app = context;
|
||||
|
||||
if(app->error == BadBleAppErrorNoFiles) {
|
||||
widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
81,
|
||||
4,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"No SD card or\napp data found.\nThis app will not\nwork without\nrequired files.");
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Back", bad_ble_scene_error_event_callback, app);
|
||||
} else if(app->error == BadBleAppErrorCloseRpc) {
|
||||
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nIs Active!");
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
3,
|
||||
30,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Disconnect from\nPC or phone to\nuse this function.");
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewWidget);
|
||||
}
|
||||
|
||||
bool bad_ble_scene_error_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBleApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == BadBleCustomEventErrorBack) {
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_ble_scene_error_on_exit(void* context) {
|
||||
BadBleApp* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
#include "../bad_ble_app_i.h"
|
||||
#include <furi_hal_power.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
static bool bad_ble_file_select(BadBleApp* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, BAD_BLE_APP_SCRIPT_EXTENSION, &I_badusb_10px);
|
||||
browser_options.base_path = BAD_BLE_APP_BASE_FOLDER;
|
||||
browser_options.skip_assets = true;
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
bool res = dialog_file_browser_show(
|
||||
bad_ble->dialogs, bad_ble->file_path, bad_ble->file_path, &browser_options);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void bad_ble_scene_file_select_on_enter(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
|
||||
if(bad_ble->bad_ble_script) {
|
||||
bad_ble_script_close(bad_ble->bad_ble_script);
|
||||
bad_ble->bad_ble_script = NULL;
|
||||
}
|
||||
|
||||
if(bad_ble_file_select(bad_ble)) {
|
||||
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneWork);
|
||||
} else {
|
||||
view_dispatcher_stop(bad_ble->view_dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
bool bad_ble_scene_file_select_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
// BadBleApp* bad_ble = context;
|
||||
return false;
|
||||
}
|
||||
|
||||
void bad_ble_scene_file_select_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
// BadBleApp* bad_ble = context;
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
#include "../bad_ble_app_i.h"
|
||||
|
||||
static void bad_ble_scene_unpair_done_popup_callback(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
scene_manager_search_and_switch_to_previous_scene(bad_ble->scene_manager, BadBleSceneConfig);
|
||||
}
|
||||
|
||||
void bad_ble_scene_unpair_done_on_enter(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
Popup* popup = bad_ble->popup;
|
||||
|
||||
popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58);
|
||||
popup_set_header(popup, "Done", 20, 19, AlignLeft, AlignBottom);
|
||||
popup_set_callback(popup, bad_ble_scene_unpair_done_popup_callback);
|
||||
popup_set_context(popup, bad_ble);
|
||||
popup_set_timeout(popup, 1000);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewPopup);
|
||||
}
|
||||
|
||||
bool bad_ble_scene_unpair_done_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBleApp* bad_ble = context;
|
||||
UNUSED(bad_ble);
|
||||
UNUSED(event);
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_ble_scene_unpair_done_on_exit(void* context) {
|
||||
BadBleApp* bad_ble = context;
|
||||
Popup* popup = bad_ble->popup;
|
||||
|
||||
popup_reset(popup);
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#include "../helpers/ducky_script.h"
|
||||
#include "../bad_ble_app_i.h"
|
||||
#include "../views/bad_ble_view.h"
|
||||
#include <furi_hal.h>
|
||||
#include "toolbox/path.h"
|
||||
|
||||
void bad_ble_scene_work_button_callback(InputKey key, void* context) {
|
||||
furi_assert(context);
|
||||
BadBleApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, key);
|
||||
}
|
||||
|
||||
bool bad_ble_scene_work_on_event(void* context, SceneManagerEvent event) {
|
||||
BadBleApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InputKeyLeft) {
|
||||
if(bad_ble_view_is_idle_state(app->bad_ble_view)) {
|
||||
bad_ble_script_close(app->bad_ble_script);
|
||||
app->bad_ble_script = NULL;
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, BadBleSceneConfig);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == InputKeyOk) {
|
||||
bad_ble_script_start_stop(app->bad_ble_script);
|
||||
consumed = true;
|
||||
} else if(event.event == InputKeyRight) {
|
||||
bad_ble_script_pause_resume(app->bad_ble_script);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
bad_ble_view_set_state(app->bad_ble_view, bad_ble_script_get_state(app->bad_ble_script));
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bad_ble_scene_work_on_enter(void* context) {
|
||||
BadBleApp* app = context;
|
||||
|
||||
app->bad_ble_script = bad_ble_script_open(app->file_path, app->interface);
|
||||
bad_ble_script_set_keyboard_layout(app->bad_ble_script, app->keyboard_layout);
|
||||
|
||||
FuriString* file_name;
|
||||
file_name = furi_string_alloc();
|
||||
path_extract_filename(app->file_path, file_name, true);
|
||||
bad_ble_view_set_file_name(app->bad_ble_view, furi_string_get_cstr(file_name));
|
||||
furi_string_free(file_name);
|
||||
|
||||
FuriString* layout;
|
||||
layout = furi_string_alloc();
|
||||
path_extract_filename(app->keyboard_layout, layout, true);
|
||||
bad_ble_view_set_layout(app->bad_ble_view, furi_string_get_cstr(layout));
|
||||
furi_string_free(layout);
|
||||
|
||||
bad_ble_view_set_state(app->bad_ble_view, bad_ble_script_get_state(app->bad_ble_script));
|
||||
|
||||
bad_ble_view_set_button_callback(app->bad_ble_view, bad_ble_scene_work_button_callback, app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewWork);
|
||||
}
|
||||
|
||||
void bad_ble_scene_work_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
|
@ -1,284 +0,0 @@
|
|||
#include "bad_ble_view.h"
|
||||
#include "../helpers/ducky_script.h"
|
||||
#include <toolbox/path.h>
|
||||
#include <gui/elements.h>
|
||||
#include <assets_icons.h>
|
||||
#include "bad_ble_icons.h"
|
||||
|
||||
#define MAX_NAME_LEN 64
|
||||
|
||||
struct BadBle {
|
||||
View* view;
|
||||
BadBleButtonCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char file_name[MAX_NAME_LEN];
|
||||
char layout[MAX_NAME_LEN];
|
||||
BadBleState state;
|
||||
bool pause_wait;
|
||||
uint8_t anim_frame;
|
||||
} BadBleModel;
|
||||
|
||||
static void bad_ble_draw_callback(Canvas* canvas, void* _model) {
|
||||
BadBleModel* model = _model;
|
||||
|
||||
FuriString* disp_str;
|
||||
disp_str = furi_string_alloc_set(model->file_name);
|
||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
|
||||
|
||||
if(strlen(model->layout) == 0) {
|
||||
furi_string_set(disp_str, "(default)");
|
||||
} else {
|
||||
furi_string_printf(disp_str, "(%s)", model->layout);
|
||||
}
|
||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
||||
canvas_draw_str(
|
||||
canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
|
||||
|
||||
furi_string_reset(disp_str);
|
||||
|
||||
canvas_draw_icon(canvas, 22, 24, &I_Bad_BLE_48x22);
|
||||
|
||||
BadBleWorkerState state = model->state.state;
|
||||
|
||||
if((state == BadBleStateIdle) || (state == BadBleStateDone) ||
|
||||
(state == BadBleStateNotConnected)) {
|
||||
elements_button_center(canvas, "Run");
|
||||
elements_button_left(canvas, "Config");
|
||||
} else if((state == BadBleStateRunning) || (state == BadBleStateDelay)) {
|
||||
elements_button_center(canvas, "Stop");
|
||||
if(!model->pause_wait) {
|
||||
elements_button_right(canvas, "Pause");
|
||||
}
|
||||
} else if(state == BadBleStatePaused) {
|
||||
elements_button_center(canvas, "End");
|
||||
elements_button_right(canvas, "Resume");
|
||||
} else if(state == BadBleStateWaitForBtn) {
|
||||
elements_button_center(canvas, "Press to continue");
|
||||
} else if(state == BadBleStateWillRun) {
|
||||
elements_button_center(canvas, "Cancel");
|
||||
}
|
||||
|
||||
if(state == BadBleStateNotConnected) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to device");
|
||||
} else if(state == BadBleStateWillRun) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
|
||||
} else if(state == BadBleStateFileError) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
|
||||
} else if(state == BadBleStateScriptError) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
furi_string_printf(disp_str, "line %zu", model->state.error_line);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
|
||||
furi_string_set_str(disp_str, model->state.error);
|
||||
elements_string_fit_width(canvas, disp_str, canvas_width(canvas));
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
} else if(state == BadBleStateIdle) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0");
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(state == BadBleStateRunning) {
|
||||
if(model->anim_frame == 0) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21);
|
||||
}
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
furi_string_printf(
|
||||
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(state == BadBleStateDone) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100");
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
} else if(state == BadBleStateDelay) {
|
||||
if(model->anim_frame == 0) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
|
||||
}
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
furi_string_printf(
|
||||
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
furi_string_printf(disp_str, "delay %lus", model->state.delay_remain);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
} else if((state == BadBleStatePaused) || (state == BadBleStateWaitForBtn)) {
|
||||
if(model->anim_frame == 0) {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
|
||||
}
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
furi_string_printf(
|
||||
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||
furi_string_reset(disp_str);
|
||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, "Paused");
|
||||
furi_string_reset(disp_str);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
}
|
||||
|
||||
furi_string_free(disp_str);
|
||||
}
|
||||
|
||||
static bool bad_ble_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
BadBle* bad_ble = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
consumed = true;
|
||||
furi_assert(bad_ble->callback);
|
||||
bad_ble->callback(event->key, bad_ble->context);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
with_view_model(
|
||||
bad_ble->view, BadBleModel * model, { model->pause_wait = false; }, true);
|
||||
consumed = true;
|
||||
furi_assert(bad_ble->callback);
|
||||
bad_ble->callback(event->key, bad_ble->context);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
with_view_model(
|
||||
bad_ble->view,
|
||||
BadBleModel * model,
|
||||
{
|
||||
if((model->state.state == BadBleStateRunning) ||
|
||||
(model->state.state == BadBleStateDelay)) {
|
||||
model->pause_wait = true;
|
||||
}
|
||||
},
|
||||
true);
|
||||
consumed = true;
|
||||
furi_assert(bad_ble->callback);
|
||||
bad_ble->callback(event->key, bad_ble->context);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
BadBle* bad_ble_view_alloc(void) {
|
||||
BadBle* bad_ble = malloc(sizeof(BadBle));
|
||||
|
||||
bad_ble->view = view_alloc();
|
||||
view_allocate_model(bad_ble->view, ViewModelTypeLocking, sizeof(BadBleModel));
|
||||
view_set_context(bad_ble->view, bad_ble);
|
||||
view_set_draw_callback(bad_ble->view, bad_ble_draw_callback);
|
||||
view_set_input_callback(bad_ble->view, bad_ble_input_callback);
|
||||
|
||||
return bad_ble;
|
||||
}
|
||||
|
||||
void bad_ble_view_free(BadBle* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
view_free(bad_ble->view);
|
||||
free(bad_ble);
|
||||
}
|
||||
|
||||
View* bad_ble_view_get_view(BadBle* bad_ble) {
|
||||
furi_assert(bad_ble);
|
||||
return bad_ble->view;
|
||||
}
|
||||
|
||||
void bad_ble_view_set_button_callback(
|
||||
BadBle* bad_ble,
|
||||
BadBleButtonCallback callback,
|
||||
void* context) {
|
||||
furi_assert(bad_ble);
|
||||
furi_assert(callback);
|
||||
with_view_model(
|
||||
bad_ble->view,
|
||||
BadBleModel * model,
|
||||
{
|
||||
UNUSED(model);
|
||||
bad_ble->callback = callback;
|
||||
bad_ble->context = context;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void bad_ble_view_set_file_name(BadBle* bad_ble, const char* name) {
|
||||
furi_assert(name);
|
||||
with_view_model(
|
||||
bad_ble->view,
|
||||
BadBleModel * model,
|
||||
{ strlcpy(model->file_name, name, MAX_NAME_LEN); },
|
||||
true);
|
||||
}
|
||||
|
||||
void bad_ble_view_set_layout(BadBle* bad_ble, const char* layout) {
|
||||
furi_assert(layout);
|
||||
with_view_model(
|
||||
bad_ble->view,
|
||||
BadBleModel * model,
|
||||
{ strlcpy(model->layout, layout, MAX_NAME_LEN); },
|
||||
true);
|
||||
}
|
||||
|
||||
void bad_ble_view_set_state(BadBle* bad_ble, BadBleState* st) {
|
||||
furi_assert(st);
|
||||
with_view_model(
|
||||
bad_ble->view,
|
||||
BadBleModel * model,
|
||||
{
|
||||
memcpy(&(model->state), st, sizeof(BadBleState));
|
||||
model->anim_frame ^= 1;
|
||||
if(model->state.state == BadBleStatePaused) {
|
||||
model->pause_wait = false;
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
bool bad_ble_view_is_idle_state(BadBle* bad_ble) {
|
||||
bool is_idle = false;
|
||||
with_view_model(
|
||||
bad_ble->view,
|
||||
BadBleModel * model,
|
||||
{
|
||||
if((model->state.state == BadBleStateIdle) ||
|
||||
(model->state.state == BadBleStateDone) ||
|
||||
(model->state.state == BadBleStateNotConnected)) {
|
||||
is_idle = true;
|
||||
}
|
||||
},
|
||||
false);
|
||||
return is_idle;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/ducky_script.h"
|
||||
|
||||
typedef struct BadBle BadBle;
|
||||
typedef void (*BadBleButtonCallback)(InputKey key, void* context);
|
||||
|
||||
BadBle* bad_ble_view_alloc(void);
|
||||
|
||||
void bad_ble_view_free(BadBle* bad_ble);
|
||||
|
||||
View* bad_ble_view_get_view(BadBle* bad_ble);
|
||||
|
||||
void bad_ble_view_set_button_callback(
|
||||
BadBle* bad_ble,
|
||||
BadBleButtonCallback callback,
|
||||
void* context);
|
||||
|
||||
void bad_ble_view_set_file_name(BadBle* bad_ble, const char* name);
|
||||
|
||||
void bad_ble_view_set_layout(BadBle* bad_ble, const char* layout);
|
||||
|
||||
void bad_ble_view_set_state(BadBle* bad_ble, BadBleState* st);
|
||||
|
||||
bool bad_ble_view_is_idle_state(BadBle* bad_ble);
|
|
@ -0,0 +1,25 @@
|
|||
let eventLoop = require("event_loop");
|
||||
|
||||
// print a string after 1337 milliseconds
|
||||
eventLoop.subscribe(eventLoop.timer("oneshot", 1337), function (_subscription, _item) {
|
||||
print("Hi after 1337 ms");
|
||||
});
|
||||
|
||||
// count up to 5 with a delay of 100ms between increments
|
||||
eventLoop.subscribe(eventLoop.timer("periodic", 100), function (subscription, _item, counter) {
|
||||
print("Counter two:", counter);
|
||||
if (counter === 5)
|
||||
subscription.cancel();
|
||||
return [counter + 1];
|
||||
}, 0);
|
||||
|
||||
// count up to 15 with a delay of 100ms between increments
|
||||
// and stop the program when the count reaches 15
|
||||
eventLoop.subscribe(eventLoop.timer("periodic", 100), function (subscription, _item, event_loop, counter) {
|
||||
print("Counter one:", counter);
|
||||
if (counter === 15)
|
||||
event_loop.stop();
|
||||
return [event_loop, counter + 1];
|
||||
}, eventLoop, 0);
|
||||
|
||||
eventLoop.run();
|
57
applications/system/js_app/examples/apps/Scripts/gpio.js
Normal file
57
applications/system/js_app/examples/apps/Scripts/gpio.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
let eventLoop = require("event_loop");
|
||||
let gpio = require("gpio");
|
||||
|
||||
// initialize pins
|
||||
let led = gpio.get("pc3"); // same as `gpio.get(7)`
|
||||
let pot = gpio.get("pc0"); // same as `gpio.get(16)`
|
||||
let button = gpio.get("pc1"); // same as `gpio.get(15)`
|
||||
led.init({ direction: "out", outMode: "push_pull" });
|
||||
pot.init({ direction: "in", inMode: "analog" });
|
||||
button.init({ direction: "in", pull: "up", inMode: "interrupt", edge: "falling" });
|
||||
|
||||
// blink led
|
||||
print("Commencing blinking (PC3)");
|
||||
eventLoop.subscribe(eventLoop.timer("periodic", 1000), function (_, _item, led, state) {
|
||||
led.write(state);
|
||||
return [led, !state];
|
||||
}, led, true);
|
||||
|
||||
// read potentiometer when button is pressed
|
||||
print("Press the button (PC1)");
|
||||
eventLoop.subscribe(button.interrupt(), function (_, _item, pot) {
|
||||
print("PC0 is at", pot.read_analog(), "mV");
|
||||
}, pot);
|
||||
|
||||
// the program will just exit unless this is here
|
||||
eventLoop.run();
|
||||
|
||||
// possible pins https://docs.flipper.net/gpio-and-modules#miFsS
|
||||
// "PA7" aka 2
|
||||
// "PA6" aka 3
|
||||
// "PA4" aka 4
|
||||
// "PB3" aka 5
|
||||
// "PB2" aka 6
|
||||
// "PC3" aka 7
|
||||
// "PA14" aka 10
|
||||
// "PA13" aka 12
|
||||
// "PB6" aka 13
|
||||
// "PB7" aka 14
|
||||
// "PC1" aka 15
|
||||
// "PC0" aka 16
|
||||
// "PB14" aka 17
|
||||
|
||||
// possible modes
|
||||
// { direction: "out", outMode: "push_pull" }
|
||||
// { direction: "out", outMode: "open_drain" }
|
||||
// { direction: "out", outMode: "push_pull", altFn: true }
|
||||
// { direction: "out", outMode: "open_drain", altFn: true }
|
||||
// { direction: "in", inMode: "analog" }
|
||||
// { direction: "in", inMode: "plain_digital" }
|
||||
// { direction: "in", inMode: "interrupt", edge: "rising" }
|
||||
// { direction: "in", inMode: "interrupt", edge: "falling" }
|
||||
// { direction: "in", inMode: "interrupt", edge: "both" }
|
||||
// { direction: "in", inMode: "event", edge: "rising" }
|
||||
// { direction: "in", inMode: "event", edge: "falling" }
|
||||
// { direction: "in", inMode: "event", edge: "both" }
|
||||
// all variants support an optional `pull` field which can either be undefined,
|
||||
// "up" or "down"
|
77
applications/system/js_app/examples/apps/Scripts/gui.js
Normal file
77
applications/system/js_app/examples/apps/Scripts/gui.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
// import modules
|
||||
let eventLoop = require("event_loop");
|
||||
let gui = require("gui");
|
||||
let loadingView = require("gui/loading");
|
||||
let submenuView = require("gui/submenu");
|
||||
let emptyView = require("gui/empty_screen");
|
||||
let textInputView = require("gui/text_input");
|
||||
let textBoxView = require("gui/text_box");
|
||||
let dialogView = require("gui/dialog");
|
||||
|
||||
// declare view instances
|
||||
let views = {
|
||||
loading: loadingView.make(),
|
||||
empty: emptyView.make(),
|
||||
keyboard: textInputView.makeWith({
|
||||
header: "Enter your name",
|
||||
minLength: 0,
|
||||
maxLength: 32,
|
||||
}),
|
||||
helloDialog: dialogView.makeWith({
|
||||
center: "Hi Flipper! :)",
|
||||
}),
|
||||
longText: textBoxView.makeWith({
|
||||
text: "This is a very long string that demonstrates the TextBox view. Use the D-Pad to scroll backwards and forwards.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse rhoncus est malesuada quam egestas ultrices. Maecenas non eros a nulla eleifend vulputate et ut risus. Quisque in mauris mattis, venenatis risus eget, aliquam diam. Fusce pretium feugiat mauris, ut faucibus ex volutpat in. Phasellus volutpat ex sed gravida consectetur. Aliquam sed lectus feugiat, tristique lectus et, bibendum lacus. Ut sit amet augue eu sapien elementum aliquam quis vitae tortor. Vestibulum quis commodo odio. In elementum fermentum massa, eu pellentesque nibh cursus at. Integer eleifend lacus nec purus elementum sodales. Nulla elementum neque urna, non vulputate massa semper sed. Fusce ut nisi vitae dui blandit congue pretium vitae turpis.",
|
||||
}),
|
||||
demos: submenuView.makeWith({
|
||||
header: "Choose a demo",
|
||||
items: [
|
||||
"Hourglass screen",
|
||||
"Empty screen",
|
||||
"Text input & Dialog",
|
||||
"Text box",
|
||||
"Exit app",
|
||||
],
|
||||
}),
|
||||
};
|
||||
|
||||
// demo selector
|
||||
eventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, views) {
|
||||
if (index === 0) {
|
||||
gui.viewDispatcher.switchTo(views.loading);
|
||||
// the loading view captures all back events, preventing our navigation callback from firing
|
||||
// switch to the demo chooser after a second
|
||||
eventLoop.subscribe(eventLoop.timer("oneshot", 1000), function (_sub, _, gui, views) {
|
||||
gui.viewDispatcher.switchTo(views.demos);
|
||||
}, gui, views);
|
||||
} else if (index === 1) {
|
||||
gui.viewDispatcher.switchTo(views.empty);
|
||||
} else if (index === 2) {
|
||||
gui.viewDispatcher.switchTo(views.keyboard);
|
||||
} else if (index === 3) {
|
||||
gui.viewDispatcher.switchTo(views.longText);
|
||||
} else if (index === 4) {
|
||||
eventLoop.stop();
|
||||
}
|
||||
}, gui, eventLoop, views);
|
||||
|
||||
// say hi after keyboard input
|
||||
eventLoop.subscribe(views.keyboard.input, function (_sub, name, gui, views) {
|
||||
views.helloDialog.set("text", "Hi " + name + "! :)");
|
||||
gui.viewDispatcher.switchTo(views.helloDialog);
|
||||
}, gui, views);
|
||||
|
||||
// go back after the greeting dialog
|
||||
eventLoop.subscribe(views.helloDialog.input, function (_sub, button, gui, views) {
|
||||
if (button === "center")
|
||||
gui.viewDispatcher.switchTo(views.demos);
|
||||
}, gui, views);
|
||||
|
||||
// go to the demo chooser screen when the back key is pressed
|
||||
eventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, gui, views) {
|
||||
gui.viewDispatcher.switchTo(views.demos);
|
||||
}, gui, views);
|
||||
|
||||
// run UI
|
||||
gui.viewDispatcher.switchTo(views.demos);
|
||||
eventLoop.run();
|
|
@ -2,6 +2,8 @@ let badusb = require("badusb");
|
|||
let notify = require("notification");
|
||||
let flipper = require("flipper");
|
||||
let dialog = require("dialog");
|
||||
let gui = require("gui");
|
||||
let dialog = require("gui/dialog");
|
||||
|
||||
badusb.setup({
|
||||
vid: 0xAAAA,
|
||||
|
@ -10,7 +12,13 @@ badusb.setup({
|
|||
prod_name: "Zero",
|
||||
layout_path: "/ext/badusb/assets/layouts/en-US.kl"
|
||||
});
|
||||
dialog.message("BadUSB demo", "Press OK to start");
|
||||
let views = {
|
||||
dialog: dialog.makeWith({
|
||||
header: "BadUSB demo",
|
||||
text: "Press OK to start",
|
||||
center: "Start",
|
||||
}),
|
||||
};
|
||||
|
||||
if (badusb.isConnected()) {
|
||||
notify.blink("green", "short");
|
||||
|
|
|
@ -6,4 +6,4 @@ print("2");
|
|||
delay(1000)
|
||||
print("3");
|
||||
delay(1000)
|
||||
print("end");
|
||||
print("end");
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
let dialog = require("dialog");
|
||||
|
||||
let result1 = dialog.message("Dialog demo", "Press OK to start");
|
||||
print(result1);
|
||||
|
||||
let dialog_params = ({
|
||||
header: "Test_header",
|
||||
text: "Test_text",
|
||||
button_left: "Left",
|
||||
button_right: "Files",
|
||||
button_center: "OK"
|
||||
});
|
||||
|
||||
let result2 = dialog.custom(dialog_params);
|
||||
if (result2 === "") {
|
||||
print("Back is pressed");
|
||||
} else if (result2 === "Files") {
|
||||
let result3 = dialog.pickFile("/ext", "*");
|
||||
print("Selected", result3);
|
||||
} else {
|
||||
print(result2, "is pressed");
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
let math = load("/ext/apps/Scripts/load_api.js");
|
||||
let result = math.add(5, 10);
|
||||
print(result);
|
||||
print(result);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
({
|
||||
add: function (a, b) { return a + b; },
|
||||
})
|
||||
})
|
||||
|
|
|
@ -22,48 +22,3 @@ print("math.sign(-5):", math.sign(-5));
|
|||
print("math.sin(math.PI/2):", math.sin(math.PI / 2));
|
||||
print("math.sqrt(25):", math.sqrt(25));
|
||||
print("math.trunc(5.7):", math.trunc(5.7));
|
||||
|
||||
// Unit tests. Please add more if you have time and knowledge.
|
||||
// math.EPSILON on Flipper Zero is 2.22044604925031308085e-16
|
||||
|
||||
let succeeded = 0;
|
||||
let failed = 0;
|
||||
|
||||
function test(text, result, expected, epsilon) {
|
||||
let is_equal = math.is_equal(result, expected, epsilon);
|
||||
if (is_equal) {
|
||||
succeeded += 1;
|
||||
} else {
|
||||
failed += 1;
|
||||
print(text, "expected", expected, "got", result);
|
||||
}
|
||||
}
|
||||
|
||||
test("math.abs(5)", math.abs(-5), 5, math.EPSILON);
|
||||
test("math.abs(0.5)", math.abs(-0.5), 0.5, math.EPSILON);
|
||||
test("math.abs(5)", math.abs(5), 5, math.EPSILON);
|
||||
test("math.abs(-0.5)", math.abs(0.5), 0.5, math.EPSILON);
|
||||
test("math.acos(0.5)", math.acos(0.5), 1.0471975511965976, math.EPSILON);
|
||||
test("math.acosh(2)", math.acosh(2), 1.3169578969248166, math.EPSILON);
|
||||
test("math.asin(0.5)", math.asin(0.5), 0.5235987755982988, math.EPSILON);
|
||||
test("math.asinh(2)", math.asinh(2), 1.4436354751788103, math.EPSILON);
|
||||
test("math.atan(1)", math.atan(1), 0.7853981633974483, math.EPSILON);
|
||||
test("math.atan2(1, 1)", math.atan2(1, 1), 0.7853981633974483, math.EPSILON);
|
||||
test("math.atanh(0.5)", math.atanh(0.5), 0.5493061443340549, math.EPSILON);
|
||||
test("math.cbrt(27)", math.cbrt(27), 3, math.EPSILON);
|
||||
test("math.ceil(5.3)", math.ceil(5.3), 6, math.EPSILON);
|
||||
test("math.clz32(1)", math.clz32(1), 31, math.EPSILON);
|
||||
test("math.floor(5.7)", math.floor(5.7), 5, math.EPSILON);
|
||||
test("math.max(3, 5)", math.max(3, 5), 5, math.EPSILON);
|
||||
test("math.min(3, 5)", math.min(3, 5), 3, math.EPSILON);
|
||||
test("math.pow(2, 3)", math.pow(2, 3), 8, math.EPSILON);
|
||||
test("math.sign(-5)", math.sign(-5), -1, math.EPSILON);
|
||||
test("math.sqrt(25)", math.sqrt(25), 5, math.EPSILON);
|
||||
test("math.trunc(5.7)", math.trunc(5.7), 5, math.EPSILON);
|
||||
test("math.cos(math.PI)", math.cos(math.PI), -1, math.EPSILON * 18); // Error 3.77475828372553223744e-15
|
||||
test("math.exp(1)", math.exp(1), 2.718281828459045, math.EPSILON * 2); // Error 4.44089209850062616169e-16
|
||||
test("math.sin(math.PI / 2)", math.sin(math.PI / 2), 1, math.EPSILON * 4.5); // Error 9.99200722162640886381e-16
|
||||
|
||||
if (failed > 0) {
|
||||
print("!!!", failed, "Unit tests failed !!!");
|
||||
}
|
|
@ -6,4 +6,4 @@ delay(1000);
|
|||
for (let i = 0; i < 10; i++) {
|
||||
notify.blink("red", "short");
|
||||
delay(500);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
let submenu = require("submenu");
|
||||
|
||||
submenu.addItem("Item 1", 0);
|
||||
submenu.addItem("Item 2", 1);
|
||||
submenu.addItem("Item 3", 2);
|
||||
|
||||
submenu.setHeader("Select an option:");
|
||||
|
||||
let result = submenu.show();
|
||||
// Returns undefined when pressing back
|
||||
print("Result:", result);
|
|
@ -1,30 +0,0 @@
|
|||
let textbox = require("textbox");
|
||||
|
||||
// You should set config before adding text
|
||||
// Focus (start / end), Font (text / hex)
|
||||
textbox.setConfig("end", "text");
|
||||
|
||||
// Can make sure it's cleared before showing, in case of reusing in same script
|
||||
// (Closing textbox already clears the text, but maybe you added more in a loop for example)
|
||||
textbox.clearText();
|
||||
|
||||
// Add default text
|
||||
textbox.addText("Example dynamic updating textbox\n");
|
||||
|
||||
// Non-blocking, can keep updating text after, can close in JS or in GUI
|
||||
textbox.show();
|
||||
|
||||
let i = 0;
|
||||
while (textbox.isOpen() && i < 20) {
|
||||
print("console", i++);
|
||||
|
||||
// Add text to textbox buffer
|
||||
textbox.addText("textbox " + to_string(i) + "\n");
|
||||
|
||||
delay(500);
|
||||
}
|
||||
|
||||
// If not closed by user (instead i < 20 is false above), close forcefully
|
||||
if (textbox.isOpen()) {
|
||||
textbox.close();
|
||||
}
|
|
@ -2,11 +2,11 @@ let serial = require("serial");
|
|||
serial.setup("usart", 230400);
|
||||
|
||||
while (1) {
|
||||
let rx_data = serial.readBytes(1, 0);
|
||||
let rx_data = serial.readBytes(1, 1000);
|
||||
if (rx_data !== undefined) {
|
||||
serial.write(rx_data);
|
||||
let data_view = Uint8Array(rx_data);
|
||||
print("0x" + to_hex_string(data_view[0]));
|
||||
print("0x" + toString(data_view[0], 16));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,4 +78,4 @@ export declare function print(string: string, delay?: number): void;
|
|||
* @param string The string to print
|
||||
* @param delay How many milliseconds to wait between key presses
|
||||
*/
|
||||
export declare function println(): void;
|
||||
export declare function println(string: string, delay?: number): void;
|
||||
|
|
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
|
@ -119,8 +119,6 @@ FuriStatus furi_timer_stop(FuriTimer* instance) {
|
|||
|
||||
furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS);
|
||||
|
||||
furi_timer_flush();
|
||||
|
||||
return FuriStatusOk;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,37 @@ Iso14443_4aError iso14443_4a_poller_send_block(
|
|||
return error;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_chain_block(
|
||||
Iso14443_4aPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
iso14443_4_layer_set_i_block(instance->iso14443_4_layer, true, false);
|
||||
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_receive_ready_block(
|
||||
Iso14443_4aPoller* instance,
|
||||
bool acknowledged,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
bool CID_present = bit_buffer_get_size_bytes(tx_buffer) != 0;
|
||||
iso14443_4_layer_set_r_block(instance->iso14443_4_layer, acknowledged, CID_present);
|
||||
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_supervisory_block(
|
||||
Iso14443_4aPoller* instance,
|
||||
bool deselect,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
bool CID_present = bit_buffer_get_size_bytes(tx_buffer) != 0;
|
||||
iso14443_4_layer_set_s_block(instance->iso14443_4_layer, deselect, CID_present);
|
||||
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext(
|
||||
Iso14443_4aPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
|
@ -164,34 +195,3 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext(
|
|||
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_chain_block(
|
||||
Iso14443_4aPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
iso14443_4_layer_set_i_block(instance->iso14443_4_layer, true, false);
|
||||
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_receive_ready_block(
|
||||
Iso14443_4aPoller* instance,
|
||||
bool acknowledged,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
bool CID_present = bit_buffer_get_size_bytes(tx_buffer) != 0;
|
||||
iso14443_4_layer_set_r_block(instance->iso14443_4_layer, acknowledged, CID_present);
|
||||
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_supervisory_block(
|
||||
Iso14443_4aPoller* instance,
|
||||
bool deselect,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
bool CID_present = bit_buffer_get_size_bytes(tx_buffer) != 0;
|
||||
iso14443_4_layer_set_s_block(instance->iso14443_4_layer, deselect, CID_present);
|
||||
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
|
||||
return error;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue