mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
merge ofw pr 4136 [ci skip]
BadUSB: Full USB/BLE parameter customization, UI improvements, and more by Willy-JL
This commit is contained in:
@@ -7,7 +7,7 @@ App(
|
|||||||
icon="A_BadUsb_14",
|
icon="A_BadUsb_14",
|
||||||
order=70,
|
order=70,
|
||||||
resources="resources",
|
resources="resources",
|
||||||
fap_libs=["assets", "ble_profile"],
|
fap_libs=["assets"],
|
||||||
fap_icon="icon.png",
|
fap_icon="icon.png",
|
||||||
fap_category="USB",
|
fap_category="USB",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,52 +31,123 @@ static void bad_usb_app_tick_event_callback(void* context) {
|
|||||||
static void bad_usb_load_settings(BadUsbApp* app) {
|
static void bad_usb_load_settings(BadUsbApp* app) {
|
||||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
FlipperFormat* fff = flipper_format_file_alloc(storage);
|
FlipperFormat* fff = flipper_format_file_alloc(storage);
|
||||||
bool state = false;
|
bool loaded = false;
|
||||||
|
|
||||||
|
BadUsbHidConfig* hid_cfg = &app->user_hid_cfg;
|
||||||
FuriString* temp_str = furi_string_alloc();
|
FuriString* temp_str = furi_string_alloc();
|
||||||
uint32_t version = 0;
|
uint32_t temp_uint = 0;
|
||||||
uint32_t interface = 0;
|
|
||||||
|
|
||||||
if(flipper_format_file_open_existing(fff, BAD_USB_SETTINGS_PATH)) {
|
if(flipper_format_file_open_existing(fff, BAD_USB_SETTINGS_PATH)) {
|
||||||
do {
|
do {
|
||||||
if(!flipper_format_read_header(fff, temp_str, &version)) break;
|
if(!flipper_format_read_header(fff, temp_str, &temp_uint)) break;
|
||||||
if((strcmp(furi_string_get_cstr(temp_str), BAD_USB_SETTINGS_FILE_TYPE) != 0) ||
|
if((strcmp(furi_string_get_cstr(temp_str), BAD_USB_SETTINGS_FILE_TYPE) != 0) ||
|
||||||
(version != BAD_USB_SETTINGS_VERSION))
|
(temp_uint != BAD_USB_SETTINGS_VERSION))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(!flipper_format_read_string(fff, "layout", temp_str)) break;
|
if(flipper_format_read_string(fff, "layout", temp_str)) {
|
||||||
if(!flipper_format_read_uint32(fff, "interface", &interface, 1)) break;
|
|
||||||
if(interface > BadUsbHidInterfaceBle) break;
|
|
||||||
|
|
||||||
state = true;
|
|
||||||
} while(0);
|
|
||||||
}
|
|
||||||
flipper_format_free(fff);
|
|
||||||
furi_record_close(RECORD_STORAGE);
|
|
||||||
|
|
||||||
if(state) {
|
|
||||||
furi_string_set(app->keyboard_layout, temp_str);
|
furi_string_set(app->keyboard_layout, temp_str);
|
||||||
app->interface = interface;
|
|
||||||
|
|
||||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
|
||||||
FileInfo layout_file_info;
|
FileInfo layout_file_info;
|
||||||
FS_Error file_check_err = storage_common_stat(
|
FS_Error file_check_err = storage_common_stat(
|
||||||
fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
|
storage, 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)) {
|
if((file_check_err != FSE_OK) || (layout_file_info.size != 256)) {
|
||||||
furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);
|
furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);
|
furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);
|
||||||
app->interface = BadUsbHidInterfaceUsb;
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(fff, "interface", &temp_uint, 1) ||
|
||||||
|
temp_uint >= BadUsbHidInterfaceMAX) {
|
||||||
|
temp_uint = BadUsbHidInterfaceUsb;
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
app->interface = temp_uint;
|
||||||
|
|
||||||
|
if(!flipper_format_read_bool(fff, "ble_bonding", &hid_cfg->ble.bonding, 1)) {
|
||||||
|
hid_cfg->ble.bonding = true;
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(fff, "ble_pairing", &temp_uint, 1) ||
|
||||||
|
temp_uint >= GapPairingCount) {
|
||||||
|
temp_uint = GapPairingPinCodeVerifyYesNo;
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
hid_cfg->ble.pairing = temp_uint;
|
||||||
|
|
||||||
|
if(flipper_format_read_string(fff, "ble_name", temp_str)) {
|
||||||
|
strlcpy(
|
||||||
|
hid_cfg->ble.name, furi_string_get_cstr(temp_str), sizeof(hid_cfg->ble.name));
|
||||||
|
} else {
|
||||||
|
hid_cfg->ble.name[0] = '\0';
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_read_hex(
|
||||||
|
fff, "ble_mac", hid_cfg->ble.mac, sizeof(hid_cfg->ble.mac))) {
|
||||||
|
memset(hid_cfg->ble.mac, 0, sizeof(hid_cfg->ble.mac));
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flipper_format_read_string(fff, "usb_manuf", temp_str)) {
|
||||||
|
strlcpy(
|
||||||
|
hid_cfg->usb.manuf,
|
||||||
|
furi_string_get_cstr(temp_str),
|
||||||
|
sizeof(hid_cfg->usb.manuf));
|
||||||
|
} else {
|
||||||
|
hid_cfg->usb.manuf[0] = '\0';
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flipper_format_read_string(fff, "usb_product", temp_str)) {
|
||||||
|
strlcpy(
|
||||||
|
hid_cfg->usb.product,
|
||||||
|
furi_string_get_cstr(temp_str),
|
||||||
|
sizeof(hid_cfg->usb.product));
|
||||||
|
} else {
|
||||||
|
hid_cfg->usb.product[0] = '\0';
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(fff, "usb_vid", &hid_cfg->usb.vid, 1)) {
|
||||||
|
hid_cfg->usb.vid = 0;
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flipper_format_read_uint32(fff, "usb_pid", &hid_cfg->usb.pid, 1)) {
|
||||||
|
hid_cfg->usb.pid = 0;
|
||||||
|
flipper_format_rewind(fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
|
} while(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
|
|
||||||
|
flipper_format_free(fff);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
|
||||||
|
if(!loaded) {
|
||||||
|
furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);
|
||||||
|
app->interface = BadUsbHidInterfaceUsb;
|
||||||
|
hid_cfg->ble.name[0] = '\0';
|
||||||
|
memset(hid_cfg->ble.mac, 0, sizeof(hid_cfg->ble.mac));
|
||||||
|
hid_cfg->ble.bonding = true;
|
||||||
|
hid_cfg->ble.pairing = GapPairingPinCodeVerifyYesNo;
|
||||||
|
hid_cfg->usb.vid = 0;
|
||||||
|
hid_cfg->usb.pid = 0;
|
||||||
|
hid_cfg->usb.manuf[0] = '\0';
|
||||||
|
hid_cfg->usb.product[0] = '\0';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bad_usb_save_settings(BadUsbApp* app) {
|
static void bad_usb_save_settings(BadUsbApp* app) {
|
||||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
FlipperFormat* fff = flipper_format_file_alloc(storage);
|
FlipperFormat* fff = flipper_format_file_alloc(storage);
|
||||||
|
BadUsbHidConfig* hid_cfg = &app->user_hid_cfg;
|
||||||
|
uint32_t temp_uint = 0;
|
||||||
|
|
||||||
if(flipper_format_file_open_always(fff, BAD_USB_SETTINGS_PATH)) {
|
if(flipper_format_file_open_always(fff, BAD_USB_SETTINGS_PATH)) {
|
||||||
do {
|
do {
|
||||||
@@ -84,9 +155,19 @@ static void bad_usb_save_settings(BadUsbApp* app) {
|
|||||||
fff, BAD_USB_SETTINGS_FILE_TYPE, BAD_USB_SETTINGS_VERSION))
|
fff, BAD_USB_SETTINGS_FILE_TYPE, BAD_USB_SETTINGS_VERSION))
|
||||||
break;
|
break;
|
||||||
if(!flipper_format_write_string(fff, "layout", app->keyboard_layout)) break;
|
if(!flipper_format_write_string(fff, "layout", app->keyboard_layout)) break;
|
||||||
uint32_t interface_id = app->interface;
|
temp_uint = app->interface;
|
||||||
if(!flipper_format_write_uint32(fff, "interface", (const uint32_t*)&interface_id, 1))
|
if(!flipper_format_write_uint32(fff, "interface", &temp_uint, 1)) break;
|
||||||
|
if(!flipper_format_write_bool(fff, "ble_bonding", &hid_cfg->ble.bonding, 1)) break;
|
||||||
|
temp_uint = hid_cfg->ble.pairing;
|
||||||
|
if(!flipper_format_write_uint32(fff, "ble_pairing", &temp_uint, 1)) break;
|
||||||
|
if(!flipper_format_write_string_cstr(fff, "ble_name", hid_cfg->ble.name)) break;
|
||||||
|
if(!flipper_format_write_hex(
|
||||||
|
fff, "ble_mac", (uint8_t*)&hid_cfg->ble.mac, sizeof(hid_cfg->ble.mac)))
|
||||||
break;
|
break;
|
||||||
|
if(!flipper_format_write_string_cstr(fff, "usb_manuf", hid_cfg->usb.manuf)) break;
|
||||||
|
if(!flipper_format_write_string_cstr(fff, "usb_product", hid_cfg->usb.product)) break;
|
||||||
|
if(!flipper_format_write_uint32(fff, "usb_vid", &hid_cfg->usb.vid, 1)) break;
|
||||||
|
if(!flipper_format_write_uint32(fff, "usb_pid", &hid_cfg->usb.pid, 1)) break;
|
||||||
} while(0);
|
} while(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +202,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
|
|||||||
|
|
||||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||||
view_dispatcher_set_tick_event_callback(
|
view_dispatcher_set_tick_event_callback(
|
||||||
app->view_dispatcher, bad_usb_app_tick_event_callback, 500);
|
app->view_dispatcher, bad_usb_app_tick_event_callback, 250);
|
||||||
view_dispatcher_set_custom_event_callback(
|
view_dispatcher_set_custom_event_callback(
|
||||||
app->view_dispatcher, bad_usb_app_custom_event_callback);
|
app->view_dispatcher, bad_usb_app_custom_event_callback);
|
||||||
view_dispatcher_set_navigation_event_callback(
|
view_dispatcher_set_navigation_event_callback(
|
||||||
@@ -146,6 +227,14 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
|
|||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher, BadUsbAppViewWork, bad_usb_view_get_view(app->bad_usb_view));
|
app->view_dispatcher, BadUsbAppViewWork, bad_usb_view_get_view(app->bad_usb_view));
|
||||||
|
|
||||||
|
app->text_input = text_input_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, BadUsbAppViewTextInput, text_input_get_view(app->text_input));
|
||||||
|
|
||||||
|
app->byte_input = byte_input_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, BadUsbAppViewByteInput, byte_input_get_view(app->byte_input));
|
||||||
|
|
||||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||||
|
|
||||||
if(furi_hal_usb_is_locked()) {
|
if(furi_hal_usb_is_locked()) {
|
||||||
@@ -157,6 +246,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
|
|||||||
furi_check(furi_hal_usb_set_config(NULL, NULL));
|
furi_check(furi_hal_usb_set_config(NULL, NULL));
|
||||||
|
|
||||||
if(!furi_string_empty(app->file_path)) {
|
if(!furi_string_empty(app->file_path)) {
|
||||||
|
scene_manager_set_scene_state(app->scene_manager, BadUsbSceneWork, true);
|
||||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneWork);
|
scene_manager_next_scene(app->scene_manager, BadUsbSceneWork);
|
||||||
} else {
|
} else {
|
||||||
furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER);
|
furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER);
|
||||||
@@ -191,6 +281,14 @@ void bad_usb_app_free(BadUsbApp* app) {
|
|||||||
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig);
|
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig);
|
||||||
variable_item_list_free(app->var_item_list);
|
variable_item_list_free(app->var_item_list);
|
||||||
|
|
||||||
|
// Text Input
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewTextInput);
|
||||||
|
text_input_free(app->text_input);
|
||||||
|
|
||||||
|
// Byte Input
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewByteInput);
|
||||||
|
byte_input_free(app->byte_input);
|
||||||
|
|
||||||
// View dispatcher
|
// View dispatcher
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
scene_manager_free(app->scene_manager);
|
scene_manager_free(app->scene_manager);
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
#include <notification/notification_messages.h>
|
#include <notification/notification_messages.h>
|
||||||
#include <gui/modules/variable_item_list.h>
|
#include <gui/modules/variable_item_list.h>
|
||||||
|
#include <gui/modules/text_input.h>
|
||||||
|
#include <gui/modules/byte_input.h>
|
||||||
#include <gui/modules/widget.h>
|
#include <gui/modules/widget.h>
|
||||||
#include <gui/modules/popup.h>
|
#include <gui/modules/popup.h>
|
||||||
#include "views/bad_usb_view.h"
|
#include "views/bad_usb_view.h"
|
||||||
@@ -36,6 +38,13 @@ struct BadUsbApp {
|
|||||||
Widget* widget;
|
Widget* widget;
|
||||||
Popup* popup;
|
Popup* popup;
|
||||||
VariableItemList* var_item_list;
|
VariableItemList* var_item_list;
|
||||||
|
TextInput* text_input;
|
||||||
|
ByteInput* byte_input;
|
||||||
|
|
||||||
|
char ble_name_buf[FURI_HAL_BT_ADV_NAME_LENGTH];
|
||||||
|
uint8_t ble_mac_buf[GAP_MAC_ADDR_SIZE];
|
||||||
|
char usb_name_buf[HID_MANUF_PRODUCT_NAME_LEN];
|
||||||
|
uint16_t usb_vidpid_buf[2];
|
||||||
|
|
||||||
BadUsbAppError error;
|
BadUsbAppError error;
|
||||||
FuriString* file_path;
|
FuriString* file_path;
|
||||||
@@ -44,6 +53,8 @@ struct BadUsbApp {
|
|||||||
BadUsbScript* bad_usb_script;
|
BadUsbScript* bad_usb_script;
|
||||||
|
|
||||||
BadUsbHidInterface interface;
|
BadUsbHidInterface interface;
|
||||||
|
BadUsbHidConfig user_hid_cfg;
|
||||||
|
BadUsbHidConfig script_hid_cfg;
|
||||||
FuriHalUsbInterface* usb_if_prev;
|
FuriHalUsbInterface* usb_if_prev;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,6 +63,8 @@ typedef enum {
|
|||||||
BadUsbAppViewPopup,
|
BadUsbAppViewPopup,
|
||||||
BadUsbAppViewWork,
|
BadUsbAppViewWork,
|
||||||
BadUsbAppViewConfig,
|
BadUsbAppViewConfig,
|
||||||
|
BadUsbAppViewByteInput,
|
||||||
|
BadUsbAppViewTextInput,
|
||||||
} BadUsbAppView;
|
} BadUsbAppView;
|
||||||
|
|
||||||
void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface);
|
void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "bad_usb_hid.h"
|
#include "bad_usb_hid.h"
|
||||||
#include <extra_profiles/hid_profile.h>
|
#include "ble_hid_profile.h"
|
||||||
#include <bt/bt_service/bt.h>
|
#include <bt/bt_service/bt.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
|
|
||||||
@@ -7,8 +7,14 @@
|
|||||||
|
|
||||||
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
|
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
|
||||||
|
|
||||||
void* hid_usb_init(FuriHalUsbHidConfig* hid_cfg) {
|
void hid_usb_adjust_config(BadUsbHidConfig* hid_cfg) {
|
||||||
furi_check(furi_hal_usb_set_config(&usb_hid, hid_cfg));
|
if(hid_cfg->usb.vid == 0) hid_cfg->usb.vid = HID_VID_DEFAULT;
|
||||||
|
if(hid_cfg->usb.pid == 0) hid_cfg->usb.pid = HID_PID_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* hid_usb_init(BadUsbHidConfig* hid_cfg) {
|
||||||
|
hid_usb_adjust_config(hid_cfg);
|
||||||
|
furi_check(furi_hal_usb_set_config(&usb_hid, &hid_cfg->usb));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +92,7 @@ uint8_t hid_usb_get_led_state(void* inst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const BadUsbHidApi hid_api_usb = {
|
static const BadUsbHidApi hid_api_usb = {
|
||||||
|
.adjust_config = hid_usb_adjust_config,
|
||||||
.init = hid_usb_init,
|
.init = hid_usb_init,
|
||||||
.deinit = hid_usb_deinit,
|
.deinit = hid_usb_deinit,
|
||||||
.set_state_callback = hid_usb_set_state_callback,
|
.set_state_callback = hid_usb_set_state_callback,
|
||||||
@@ -111,11 +118,6 @@ typedef struct {
|
|||||||
bool is_connected;
|
bool is_connected;
|
||||||
} BleHidInstance;
|
} 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) {
|
static void hid_ble_connection_status_callback(BtStatus status, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
BleHidInstance* ble_hid = context;
|
BleHidInstance* ble_hid = context;
|
||||||
@@ -125,8 +127,38 @@ static void hid_ble_connection_status_callback(BtStatus status, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) {
|
void hid_ble_adjust_config(BadUsbHidConfig* hid_cfg) {
|
||||||
UNUSED(hid_cfg);
|
const uint8_t* normal_mac = furi_hal_version_get_ble_mac();
|
||||||
|
uint8_t empty_mac[GAP_MAC_ADDR_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
uint8_t default_mac[GAP_MAC_ADDR_SIZE] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72}; // furi_hal_bt
|
||||||
|
if(memcmp(hid_cfg->ble.mac, empty_mac, sizeof(hid_cfg->ble.mac)) == 0 ||
|
||||||
|
memcmp(hid_cfg->ble.mac, normal_mac, sizeof(hid_cfg->ble.mac)) == 0 ||
|
||||||
|
memcmp(hid_cfg->ble.mac, default_mac, sizeof(hid_cfg->ble.mac)) == 0) {
|
||||||
|
// Derive badusb MAC from Flipper MAC
|
||||||
|
memcpy(hid_cfg->ble.mac, normal_mac, sizeof(hid_cfg->ble.mac));
|
||||||
|
hid_cfg->ble.mac[2]++;
|
||||||
|
uint16_t badusb_mac_xor = 0x0002;
|
||||||
|
hid_cfg->ble.mac[0] ^= badusb_mac_xor;
|
||||||
|
hid_cfg->ble.mac[1] ^= badusb_mac_xor >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hid_cfg->ble.name[0] == '\0') {
|
||||||
|
// Derive badusb name from Flipper name
|
||||||
|
const char* badusb_device_name_prefix = "BadUSB";
|
||||||
|
snprintf(
|
||||||
|
hid_cfg->ble.name,
|
||||||
|
sizeof(hid_cfg->ble.name),
|
||||||
|
"%s %s",
|
||||||
|
badusb_device_name_prefix,
|
||||||
|
furi_hal_version_get_name_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hid_cfg->ble.pairing >= GapPairingCount) {
|
||||||
|
hid_cfg->ble.pairing = GapPairingPinCodeVerifyYesNo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* hid_ble_init(BadUsbHidConfig* hid_cfg) {
|
||||||
BleHidInstance* ble_hid = malloc(sizeof(BleHidInstance));
|
BleHidInstance* ble_hid = malloc(sizeof(BleHidInstance));
|
||||||
ble_hid->bt = furi_record_open(RECORD_BT);
|
ble_hid->bt = furi_record_open(RECORD_BT);
|
||||||
bt_disconnect(ble_hid->bt);
|
bt_disconnect(ble_hid->bt);
|
||||||
@@ -136,7 +168,8 @@ void* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) {
|
|||||||
|
|
||||||
bt_keys_storage_set_storage_path(ble_hid->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
|
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);
|
hid_ble_adjust_config(hid_cfg);
|
||||||
|
ble_hid->profile = bt_profile_start(ble_hid->bt, ble_profile_hid, &hid_cfg->ble);
|
||||||
furi_check(ble_hid->profile);
|
furi_check(ble_hid->profile);
|
||||||
|
|
||||||
furi_hal_bt_start_advertising();
|
furi_hal_bt_start_advertising();
|
||||||
@@ -236,6 +269,7 @@ uint8_t hid_ble_get_led_state(void* inst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const BadUsbHidApi hid_api_ble = {
|
static const BadUsbHidApi hid_api_ble = {
|
||||||
|
.adjust_config = hid_ble_adjust_config,
|
||||||
.init = hid_ble_init,
|
.init = hid_ble_init,
|
||||||
.deinit = hid_ble_deinit,
|
.deinit = hid_ble_deinit,
|
||||||
.set_state_callback = hid_ble_set_state_callback,
|
.set_state_callback = hid_ble_set_state_callback,
|
||||||
|
|||||||
@@ -7,13 +7,22 @@ extern "C" {
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#include "ble_hid_profile.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BadUsbHidInterfaceUsb,
|
BadUsbHidInterfaceUsb,
|
||||||
BadUsbHidInterfaceBle,
|
BadUsbHidInterfaceBle,
|
||||||
|
BadUsbHidInterfaceMAX,
|
||||||
} BadUsbHidInterface;
|
} BadUsbHidInterface;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* (*init)(FuriHalUsbHidConfig* hid_cfg);
|
BleProfileHidParams ble;
|
||||||
|
FuriHalUsbHidConfig usb;
|
||||||
|
} BadUsbHidConfig;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void (*adjust_config)(BadUsbHidConfig* hid_cfg);
|
||||||
|
void* (*init)(BadUsbHidConfig* hid_cfg);
|
||||||
void (*deinit)(void* inst);
|
void (*deinit)(void* inst);
|
||||||
void (*set_state_callback)(void* inst, HidStateCallback cb, void* context);
|
void (*set_state_callback)(void* inst, HidStateCallback cb, void* context);
|
||||||
bool (*is_connected)(void* inst);
|
bool (*is_connected)(void* inst);
|
||||||
|
|||||||
429
applications/main/bad_usb/helpers/ble_hid_profile.c
Normal file
429
applications/main/bad_usb/helpers/ble_hid_profile.c
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
#include "ble_hid_profile.h"
|
||||||
|
|
||||||
|
// Based on <lib/ble_profile/extra_profiles/hid_profile.c>
|
||||||
|
|
||||||
|
#include <furi_hal_usb_hid.h>
|
||||||
|
#include <services/dev_info_service.h>
|
||||||
|
#include <services/battery_service.h>
|
||||||
|
#include "ble_hid_service.h"
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <usb_hid.h>
|
||||||
|
#include <ble/ble.h>
|
||||||
|
|
||||||
|
#define HID_INFO_BASE_USB_SPECIFICATION (0x0101)
|
||||||
|
#define HID_INFO_COUNTRY_CODE (0x00)
|
||||||
|
#define BLE_PROFILE_HID_INFO_FLAG_REMOTE_WAKE_MSK (0x01)
|
||||||
|
#define BLE_PROFILE_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK (0x02)
|
||||||
|
|
||||||
|
#define BLE_PROFILE_HID_KB_MAX_KEYS (6)
|
||||||
|
#define BLE_PROFILE_CONSUMER_MAX_KEYS (1)
|
||||||
|
|
||||||
|
// Report ids cant be 0
|
||||||
|
enum HidReportId {
|
||||||
|
ReportIdKeyboard = 1,
|
||||||
|
ReportIdMouse = 2,
|
||||||
|
ReportIdConsumer = 3,
|
||||||
|
};
|
||||||
|
// Report numbers corresponded to the report id with an offset of 1
|
||||||
|
enum HidInputNumber {
|
||||||
|
ReportNumberKeyboard = 0,
|
||||||
|
ReportNumberMouse = 1,
|
||||||
|
ReportNumberConsumer = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t mods;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t key[BLE_PROFILE_HID_KB_MAX_KEYS];
|
||||||
|
} FURI_PACKED FuriHalBtHidKbReport;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t btn;
|
||||||
|
int8_t x;
|
||||||
|
int8_t y;
|
||||||
|
int8_t wheel;
|
||||||
|
} FURI_PACKED FuriHalBtHidMouseReport;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t key[BLE_PROFILE_CONSUMER_MAX_KEYS];
|
||||||
|
} FURI_PACKED FuriHalBtHidConsumerReport;
|
||||||
|
|
||||||
|
// keyboard+mouse+consumer hid report
|
||||||
|
static const uint8_t ble_profile_hid_report_map_data[] = {
|
||||||
|
// Keyboard Report
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
|
HID_USAGE(HID_DESKTOP_KEYBOARD),
|
||||||
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
|
HID_REPORT_ID(ReportIdKeyboard),
|
||||||
|
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
||||||
|
HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
|
||||||
|
HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(1),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_REPORT_COUNT(8),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_COUNT(1),
|
||||||
|
HID_REPORT_SIZE(8),
|
||||||
|
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_LED),
|
||||||
|
HID_REPORT_COUNT(8),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_USAGE_MINIMUM(1),
|
||||||
|
HID_USAGE_MAXIMUM(8),
|
||||||
|
HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_COUNT(BLE_PROFILE_HID_KB_MAX_KEYS),
|
||||||
|
HID_REPORT_SIZE(8),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(101),
|
||||||
|
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
||||||
|
HID_USAGE_MINIMUM(0),
|
||||||
|
HID_USAGE_MAXIMUM(101),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
|
||||||
|
HID_END_COLLECTION,
|
||||||
|
// Mouse Report
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
|
HID_USAGE(HID_DESKTOP_MOUSE),
|
||||||
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
|
HID_USAGE(HID_DESKTOP_POINTER),
|
||||||
|
HID_COLLECTION(HID_PHYSICAL_COLLECTION),
|
||||||
|
HID_REPORT_ID(ReportIdMouse),
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_BUTTON),
|
||||||
|
HID_USAGE_MINIMUM(1),
|
||||||
|
HID_USAGE_MAXIMUM(3),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(1),
|
||||||
|
HID_REPORT_COUNT(3),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_REPORT_COUNT(5),
|
||||||
|
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
|
HID_USAGE(HID_DESKTOP_X),
|
||||||
|
HID_USAGE(HID_DESKTOP_Y),
|
||||||
|
HID_USAGE(HID_DESKTOP_WHEEL),
|
||||||
|
HID_LOGICAL_MINIMUM(-127),
|
||||||
|
HID_LOGICAL_MAXIMUM(127),
|
||||||
|
HID_REPORT_SIZE(8),
|
||||||
|
HID_REPORT_COUNT(3),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
|
||||||
|
HID_END_COLLECTION,
|
||||||
|
HID_END_COLLECTION,
|
||||||
|
// Consumer Report
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_CONSUMER),
|
||||||
|
HID_USAGE(HID_CONSUMER_CONTROL),
|
||||||
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
|
HID_REPORT_ID(ReportIdConsumer),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_RI_LOGICAL_MAXIMUM(16, 0x3FF),
|
||||||
|
HID_USAGE_MINIMUM(0),
|
||||||
|
HID_RI_USAGE_MAXIMUM(16, 0x3FF),
|
||||||
|
HID_REPORT_COUNT(BLE_PROFILE_CONSUMER_MAX_KEYS),
|
||||||
|
HID_REPORT_SIZE(16),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
|
||||||
|
HID_END_COLLECTION,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriHalBleProfileBase base;
|
||||||
|
|
||||||
|
FuriHalBtHidKbReport* kb_report;
|
||||||
|
FuriHalBtHidMouseReport* mouse_report;
|
||||||
|
FuriHalBtHidConsumerReport* consumer_report;
|
||||||
|
|
||||||
|
BleServiceBattery* battery_svc;
|
||||||
|
BleServiceDevInfo* dev_info_svc;
|
||||||
|
BleServiceHid* hid_svc;
|
||||||
|
} BleProfileHid;
|
||||||
|
_Static_assert(offsetof(BleProfileHid, base) == 0, "Wrong layout");
|
||||||
|
|
||||||
|
static FuriHalBleProfileBase* ble_profile_hid_start(FuriHalBleProfileParams profile_params) {
|
||||||
|
UNUSED(profile_params);
|
||||||
|
|
||||||
|
BleProfileHid* profile = malloc(sizeof(BleProfileHid));
|
||||||
|
|
||||||
|
profile->base.config = ble_profile_hid;
|
||||||
|
|
||||||
|
profile->battery_svc = ble_svc_battery_start(true);
|
||||||
|
profile->dev_info_svc = ble_svc_dev_info_start();
|
||||||
|
profile->hid_svc = ble_svc_hid_start();
|
||||||
|
|
||||||
|
// Configure HID Keyboard
|
||||||
|
profile->kb_report = malloc(sizeof(FuriHalBtHidKbReport));
|
||||||
|
profile->mouse_report = malloc(sizeof(FuriHalBtHidMouseReport));
|
||||||
|
profile->consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport));
|
||||||
|
|
||||||
|
// Configure Report Map characteristic
|
||||||
|
ble_svc_hid_update_report_map(
|
||||||
|
profile->hid_svc,
|
||||||
|
ble_profile_hid_report_map_data,
|
||||||
|
sizeof(ble_profile_hid_report_map_data));
|
||||||
|
// Configure HID Information characteristic
|
||||||
|
uint8_t hid_info_val[4] = {
|
||||||
|
HID_INFO_BASE_USB_SPECIFICATION & 0x00ff,
|
||||||
|
(HID_INFO_BASE_USB_SPECIFICATION & 0xff00) >> 8,
|
||||||
|
HID_INFO_COUNTRY_CODE,
|
||||||
|
BLE_PROFILE_HID_INFO_FLAG_REMOTE_WAKE_MSK |
|
||||||
|
BLE_PROFILE_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK,
|
||||||
|
};
|
||||||
|
ble_svc_hid_update_info(profile->hid_svc, hid_info_val);
|
||||||
|
|
||||||
|
return &profile->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_profile_hid_stop(FuriHalBleProfileBase* profile) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
ble_svc_battery_stop(hid_profile->battery_svc);
|
||||||
|
ble_svc_dev_info_stop(hid_profile->dev_info_svc);
|
||||||
|
ble_svc_hid_stop(hid_profile->hid_svc);
|
||||||
|
|
||||||
|
free(hid_profile->kb_report);
|
||||||
|
free(hid_profile->mouse_report);
|
||||||
|
free(hid_profile->consumer_report);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_kb_press(FuriHalBleProfileBase* profile, uint16_t button) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidKbReport* kb_report = hid_profile->kb_report;
|
||||||
|
for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) {
|
||||||
|
if(kb_report->key[i] == 0) {
|
||||||
|
kb_report->key[i] = button & 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kb_report->mods |= (button >> 8);
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberKeyboard,
|
||||||
|
(uint8_t*)kb_report,
|
||||||
|
sizeof(FuriHalBtHidKbReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_kb_release(FuriHalBleProfileBase* profile, uint16_t button) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
|
||||||
|
FuriHalBtHidKbReport* kb_report = hid_profile->kb_report;
|
||||||
|
for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) {
|
||||||
|
if(kb_report->key[i] == (button & 0xFF)) {
|
||||||
|
kb_report->key[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kb_report->mods &= ~(button >> 8);
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberKeyboard,
|
||||||
|
(uint8_t*)kb_report,
|
||||||
|
sizeof(FuriHalBtHidKbReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_kb_release_all(FuriHalBleProfileBase* profile) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidKbReport* kb_report = hid_profile->kb_report;
|
||||||
|
for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) {
|
||||||
|
kb_report->key[i] = 0;
|
||||||
|
}
|
||||||
|
kb_report->mods = 0;
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberKeyboard,
|
||||||
|
(uint8_t*)kb_report,
|
||||||
|
sizeof(FuriHalBtHidKbReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_consumer_key_press(FuriHalBleProfileBase* profile, uint16_t button) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report;
|
||||||
|
for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008
|
||||||
|
if(consumer_report->key[i] == 0) {
|
||||||
|
consumer_report->key[i] = button;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberConsumer,
|
||||||
|
(uint8_t*)consumer_report,
|
||||||
|
sizeof(FuriHalBtHidConsumerReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_consumer_key_release(FuriHalBleProfileBase* profile, uint16_t button) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report;
|
||||||
|
for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008
|
||||||
|
if(consumer_report->key[i] == button) {
|
||||||
|
consumer_report->key[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberConsumer,
|
||||||
|
(uint8_t*)consumer_report,
|
||||||
|
sizeof(FuriHalBtHidConsumerReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_consumer_key_release_all(FuriHalBleProfileBase* profile) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report;
|
||||||
|
for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008
|
||||||
|
consumer_report->key[i] = 0;
|
||||||
|
}
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberConsumer,
|
||||||
|
(uint8_t*)consumer_report,
|
||||||
|
sizeof(FuriHalBtHidConsumerReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_mouse_move(FuriHalBleProfileBase* profile, int8_t dx, int8_t dy) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;
|
||||||
|
mouse_report->x = dx;
|
||||||
|
mouse_report->y = dy;
|
||||||
|
bool state = ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberMouse,
|
||||||
|
(uint8_t*)mouse_report,
|
||||||
|
sizeof(FuriHalBtHidMouseReport));
|
||||||
|
mouse_report->x = 0;
|
||||||
|
mouse_report->y = 0;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_mouse_press(FuriHalBleProfileBase* profile, uint8_t button) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;
|
||||||
|
mouse_report->btn |= button;
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberMouse,
|
||||||
|
(uint8_t*)mouse_report,
|
||||||
|
sizeof(FuriHalBtHidMouseReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_mouse_release(FuriHalBleProfileBase* profile, uint8_t button) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;
|
||||||
|
mouse_report->btn &= ~button;
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberMouse,
|
||||||
|
(uint8_t*)mouse_report,
|
||||||
|
sizeof(FuriHalBtHidMouseReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_mouse_release_all(FuriHalBleProfileBase* profile) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;
|
||||||
|
mouse_report->btn = 0;
|
||||||
|
return ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberMouse,
|
||||||
|
(uint8_t*)mouse_report,
|
||||||
|
sizeof(FuriHalBtHidMouseReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta) {
|
||||||
|
furi_check(profile);
|
||||||
|
furi_check(profile->config == ble_profile_hid);
|
||||||
|
|
||||||
|
BleProfileHid* hid_profile = (BleProfileHid*)profile;
|
||||||
|
FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report;
|
||||||
|
mouse_report->wheel = delta;
|
||||||
|
bool state = ble_svc_hid_update_input_report(
|
||||||
|
hid_profile->hid_svc,
|
||||||
|
ReportNumberMouse,
|
||||||
|
(uint8_t*)mouse_report,
|
||||||
|
sizeof(FuriHalBtHidMouseReport));
|
||||||
|
mouse_report->wheel = 0;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AN5289: 4.7, in order to use flash controller interval must be at least 25ms + advertisement, which is 30 ms
|
||||||
|
// Since we don't use flash controller anymore interval can be lowered to 7.5ms
|
||||||
|
#define CONNECTION_INTERVAL_MIN (0x0006)
|
||||||
|
// Up to 45 ms
|
||||||
|
#define CONNECTION_INTERVAL_MAX (0x24)
|
||||||
|
|
||||||
|
static GapConfig template_config = {
|
||||||
|
.adv_service =
|
||||||
|
{
|
||||||
|
.UUID_Type = UUID_TYPE_16,
|
||||||
|
.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
|
||||||
|
},
|
||||||
|
.appearance_char = GAP_APPEARANCE_KEYBOARD,
|
||||||
|
.bonding_mode = true,
|
||||||
|
.pairing_method = GapPairingPinCodeVerifyYesNo,
|
||||||
|
.conn_param =
|
||||||
|
{
|
||||||
|
.conn_int_min = CONNECTION_INTERVAL_MIN,
|
||||||
|
.conn_int_max = CONNECTION_INTERVAL_MAX,
|
||||||
|
.slave_latency = 0,
|
||||||
|
.supervisor_timeout = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ble_profile_hid_get_config(GapConfig* config, FuriHalBleProfileParams profile_params) {
|
||||||
|
furi_check(profile_params);
|
||||||
|
BleProfileHidParams* hid_profile_params = profile_params;
|
||||||
|
|
||||||
|
furi_check(config);
|
||||||
|
memcpy(config, &template_config, sizeof(GapConfig));
|
||||||
|
|
||||||
|
// Set MAC address
|
||||||
|
memcpy(config->mac_address, hid_profile_params->mac, sizeof(config->mac_address));
|
||||||
|
|
||||||
|
// Set advertise name
|
||||||
|
config->adv_name[0] = furi_hal_version_get_ble_local_device_name_ptr()[0];
|
||||||
|
strlcpy(config->adv_name + 1, hid_profile_params->name, sizeof(config->adv_name) - 1);
|
||||||
|
|
||||||
|
// Set bonding mode
|
||||||
|
config->bonding_mode = hid_profile_params->bonding;
|
||||||
|
|
||||||
|
// Set pairing method
|
||||||
|
config->pairing_method = hid_profile_params->pairing;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const FuriHalBleProfileTemplate profile_callbacks = {
|
||||||
|
.start = ble_profile_hid_start,
|
||||||
|
.stop = ble_profile_hid_stop,
|
||||||
|
.get_gap_config = ble_profile_hid_get_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
const FuriHalBleProfileTemplate* ble_profile_hid = &profile_callbacks;
|
||||||
109
applications/main/bad_usb/helpers/ble_hid_profile.h
Normal file
109
applications/main/bad_usb/helpers/ble_hid_profile.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Based on <lib/ble_profile/extra_profiles/hid_profile.h>
|
||||||
|
|
||||||
|
#include <furi_ble/profile_interface.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional arguments to pass along with profile template as
|
||||||
|
* FuriHalBleProfileParams for tuning profile behavior
|
||||||
|
**/
|
||||||
|
typedef struct {
|
||||||
|
char name[FURI_HAL_BT_ADV_NAME_LENGTH]; /**< Full device name */
|
||||||
|
uint8_t mac[GAP_MAC_ADDR_SIZE]; /**< Full device address */
|
||||||
|
bool bonding; /**< Save paired devices */
|
||||||
|
GapPairing pairing; /**< Pairing security method */
|
||||||
|
} BleProfileHidParams;
|
||||||
|
|
||||||
|
/** Hid Keyboard Profile descriptor */
|
||||||
|
extern const FuriHalBleProfileTemplate* ble_profile_hid;
|
||||||
|
|
||||||
|
/** Press keyboard button
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param button button code from HID specification
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_kb_press(FuriHalBleProfileBase* profile, uint16_t button);
|
||||||
|
|
||||||
|
/** Release keyboard button
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param button button code from HID specification
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_kb_release(FuriHalBleProfileBase* profile, uint16_t button);
|
||||||
|
|
||||||
|
/** Release all keyboard buttons
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_kb_release_all(FuriHalBleProfileBase* profile);
|
||||||
|
|
||||||
|
/** Set the following consumer key to pressed state and send HID report
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_consumer_key_press(FuriHalBleProfileBase* profile, uint16_t button);
|
||||||
|
|
||||||
|
/** Set the following consumer key to released state and send HID report
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_consumer_key_release(FuriHalBleProfileBase* profile, uint16_t button);
|
||||||
|
|
||||||
|
/** Set consumer key to released state and send HID report
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_consumer_key_release_all(FuriHalBleProfileBase* profile);
|
||||||
|
|
||||||
|
/** Set mouse movement and send HID report
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param dx x coordinate delta
|
||||||
|
* @param dy y coordinate delta
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_mouse_move(FuriHalBleProfileBase* profile, int8_t dx, int8_t dy);
|
||||||
|
|
||||||
|
/** Set mouse button to pressed state and send HID report
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_mouse_press(FuriHalBleProfileBase* profile, uint8_t button);
|
||||||
|
|
||||||
|
/** Set mouse button to released state and send HID report
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_mouse_release(FuriHalBleProfileBase* profile, uint8_t button);
|
||||||
|
|
||||||
|
/** Set mouse button to released state and send HID report
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_mouse_release_all(FuriHalBleProfileBase* profile);
|
||||||
|
|
||||||
|
/** Set mouse wheel position and send HID report
|
||||||
|
*
|
||||||
|
* @param profile profile instance
|
||||||
|
* @param delta number of scroll steps
|
||||||
|
*/
|
||||||
|
bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
325
applications/main/bad_usb/helpers/ble_hid_service.c
Normal file
325
applications/main/bad_usb/helpers/ble_hid_service.c
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
#include "ble_hid_service.h"
|
||||||
|
|
||||||
|
// Based on <lib/ble_profile/extra_services/hid_service.c>
|
||||||
|
|
||||||
|
#include "app_common.h" // IWYU pragma: keep
|
||||||
|
#include <ble/ble.h>
|
||||||
|
#include <furi_ble/event_dispatcher.h>
|
||||||
|
#include <furi_ble/gatt.h>
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define TAG "BleHid"
|
||||||
|
|
||||||
|
#define BLE_SVC_HID_REPORT_MAP_MAX_LEN (255)
|
||||||
|
#define BLE_SVC_HID_REPORT_MAX_LEN (255)
|
||||||
|
#define BLE_SVC_HID_REPORT_REF_LEN (2)
|
||||||
|
#define BLE_SVC_HID_INFO_LEN (4)
|
||||||
|
#define BLE_SVC_HID_CONTROL_POINT_LEN (1)
|
||||||
|
|
||||||
|
#define BLE_SVC_HID_INPUT_REPORT_COUNT (3)
|
||||||
|
#define BLE_SVC_HID_OUTPUT_REPORT_COUNT (0)
|
||||||
|
#define BLE_SVC_HID_FEATURE_REPORT_COUNT (0)
|
||||||
|
#define BLE_SVC_HID_REPORT_COUNT \
|
||||||
|
(BLE_SVC_HID_INPUT_REPORT_COUNT + BLE_SVC_HID_OUTPUT_REPORT_COUNT + \
|
||||||
|
BLE_SVC_HID_FEATURE_REPORT_COUNT)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HidSvcGattCharacteristicProtocolMode = 0,
|
||||||
|
HidSvcGattCharacteristicReportMap,
|
||||||
|
HidSvcGattCharacteristicInfo,
|
||||||
|
HidSvcGattCharacteristicCtrlPoint,
|
||||||
|
HidSvcGattCharacteristicCount,
|
||||||
|
} HidSvcGattCharacteristicId;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t report_idx;
|
||||||
|
uint8_t report_type;
|
||||||
|
} HidSvcReportId;
|
||||||
|
|
||||||
|
static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes");
|
||||||
|
|
||||||
|
static const Service_UUID_t ble_svc_hid_uuid = {
|
||||||
|
.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool ble_svc_hid_char_desc_data_callback(
|
||||||
|
const void* context,
|
||||||
|
const uint8_t** data,
|
||||||
|
uint16_t* data_len) {
|
||||||
|
const HidSvcReportId* report_id = context;
|
||||||
|
*data_len = sizeof(HidSvcReportId);
|
||||||
|
if(data) {
|
||||||
|
*data = (const uint8_t*)report_id;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const void* data_ptr;
|
||||||
|
uint16_t data_len;
|
||||||
|
} HidSvcDataWrapper;
|
||||||
|
|
||||||
|
static bool ble_svc_hid_report_data_callback(
|
||||||
|
const void* context,
|
||||||
|
const uint8_t** data,
|
||||||
|
uint16_t* data_len) {
|
||||||
|
const HidSvcDataWrapper* report_data = context;
|
||||||
|
if(data) {
|
||||||
|
*data = report_data->data_ptr;
|
||||||
|
*data_len = report_data->data_len;
|
||||||
|
} else {
|
||||||
|
*data_len = BLE_SVC_HID_REPORT_MAP_MAX_LEN;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const BleGattCharacteristicParams ble_svc_hid_chars[HidSvcGattCharacteristicCount] = {
|
||||||
|
[HidSvcGattCharacteristicProtocolMode] =
|
||||||
|
{.name = "Protocol Mode",
|
||||||
|
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||||
|
.data.fixed.length = 1,
|
||||||
|
.uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID,
|
||||||
|
.uuid_type = UUID_TYPE_16,
|
||||||
|
.char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP,
|
||||||
|
.security_permissions = ATTR_PERMISSION_NONE,
|
||||||
|
.gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,
|
||||||
|
.is_variable = CHAR_VALUE_LEN_CONSTANT},
|
||||||
|
[HidSvcGattCharacteristicReportMap] =
|
||||||
|
{.name = "Report Map",
|
||||||
|
.data_prop_type = FlipperGattCharacteristicDataCallback,
|
||||||
|
.data.callback.fn = ble_svc_hid_report_data_callback,
|
||||||
|
.data.callback.context = NULL,
|
||||||
|
.uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID,
|
||||||
|
.uuid_type = UUID_TYPE_16,
|
||||||
|
.char_properties = CHAR_PROP_READ,
|
||||||
|
.security_permissions = ATTR_PERMISSION_NONE,
|
||||||
|
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
|
||||||
|
.is_variable = CHAR_VALUE_LEN_VARIABLE},
|
||||||
|
[HidSvcGattCharacteristicInfo] =
|
||||||
|
{.name = "HID Information",
|
||||||
|
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||||
|
.data.fixed.length = BLE_SVC_HID_INFO_LEN,
|
||||||
|
.data.fixed.ptr = NULL,
|
||||||
|
.uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID,
|
||||||
|
.uuid_type = UUID_TYPE_16,
|
||||||
|
.char_properties = CHAR_PROP_READ,
|
||||||
|
.security_permissions = ATTR_PERMISSION_NONE,
|
||||||
|
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
|
||||||
|
.is_variable = CHAR_VALUE_LEN_CONSTANT},
|
||||||
|
[HidSvcGattCharacteristicCtrlPoint] =
|
||||||
|
{.name = "HID Control Point",
|
||||||
|
.data_prop_type = FlipperGattCharacteristicDataFixed,
|
||||||
|
.data.fixed.length = BLE_SVC_HID_CONTROL_POINT_LEN,
|
||||||
|
.uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID,
|
||||||
|
.uuid_type = UUID_TYPE_16,
|
||||||
|
.char_properties = CHAR_PROP_WRITE_WITHOUT_RESP,
|
||||||
|
.security_permissions = ATTR_PERMISSION_NONE,
|
||||||
|
.gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE,
|
||||||
|
.is_variable = CHAR_VALUE_LEN_CONSTANT},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const BleGattCharacteristicDescriptorParams ble_svc_hid_char_descr_template = {
|
||||||
|
.uuid_type = UUID_TYPE_16,
|
||||||
|
.uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID,
|
||||||
|
.max_length = BLE_SVC_HID_REPORT_REF_LEN,
|
||||||
|
.data_callback.fn = ble_svc_hid_char_desc_data_callback,
|
||||||
|
.security_permissions = ATTR_PERMISSION_NONE,
|
||||||
|
.access_permissions = ATTR_ACCESS_READ_WRITE,
|
||||||
|
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
|
||||||
|
.is_variable = CHAR_VALUE_LEN_CONSTANT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const BleGattCharacteristicParams ble_svc_hid_report_template = {
|
||||||
|
.name = "Report",
|
||||||
|
.data_prop_type = FlipperGattCharacteristicDataCallback,
|
||||||
|
.data.callback.fn = ble_svc_hid_report_data_callback,
|
||||||
|
.data.callback.context = NULL,
|
||||||
|
.uuid.Char_UUID_16 = REPORT_CHAR_UUID,
|
||||||
|
.uuid_type = UUID_TYPE_16,
|
||||||
|
.char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY,
|
||||||
|
.security_permissions = ATTR_PERMISSION_NONE,
|
||||||
|
.gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS,
|
||||||
|
.is_variable = CHAR_VALUE_LEN_VARIABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BleServiceHid {
|
||||||
|
uint16_t svc_handle;
|
||||||
|
BleGattCharacteristicInstance chars[HidSvcGattCharacteristicCount];
|
||||||
|
BleGattCharacteristicInstance input_report_chars[BLE_SVC_HID_INPUT_REPORT_COUNT];
|
||||||
|
BleGattCharacteristicInstance output_report_chars[BLE_SVC_HID_OUTPUT_REPORT_COUNT];
|
||||||
|
BleGattCharacteristicInstance feature_report_chars[BLE_SVC_HID_FEATURE_REPORT_COUNT];
|
||||||
|
GapSvcEventHandler* event_handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
static BleEventAckStatus ble_svc_hid_event_handler(void* event, void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
|
||||||
|
BleEventAckStatus ret = BleEventNotAck;
|
||||||
|
hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);
|
||||||
|
evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data;
|
||||||
|
// aci_gatt_attribute_modified_event_rp0* attribute_modified;
|
||||||
|
|
||||||
|
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
|
||||||
|
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
|
||||||
|
// Process modification events
|
||||||
|
ret = BleEventAckFlowEnable;
|
||||||
|
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
|
||||||
|
// Process notification confirmation
|
||||||
|
ret = BleEventAckFlowEnable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
BleServiceHid* ble_svc_hid_start(void) {
|
||||||
|
BleServiceHid* hid_svc = malloc(sizeof(BleServiceHid));
|
||||||
|
|
||||||
|
// Register event handler
|
||||||
|
hid_svc->event_handler =
|
||||||
|
ble_event_dispatcher_register_svc_handler(ble_svc_hid_event_handler, hid_svc);
|
||||||
|
/**
|
||||||
|
* Add Human Interface Device Service
|
||||||
|
*/
|
||||||
|
if(!ble_gatt_service_add(
|
||||||
|
UUID_TYPE_16,
|
||||||
|
&ble_svc_hid_uuid,
|
||||||
|
PRIMARY_SERVICE,
|
||||||
|
2 + /* protocol mode */
|
||||||
|
(4 * BLE_SVC_HID_INPUT_REPORT_COUNT) + (3 * BLE_SVC_HID_OUTPUT_REPORT_COUNT) +
|
||||||
|
(3 * BLE_SVC_HID_FEATURE_REPORT_COUNT) + 1 + 2 + 2 +
|
||||||
|
2, /* Service + Report Map + HID Information + HID Control Point */
|
||||||
|
&hid_svc->svc_handle)) {
|
||||||
|
free(hid_svc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintain previously defined characteristic order
|
||||||
|
ble_gatt_characteristic_init(
|
||||||
|
hid_svc->svc_handle,
|
||||||
|
&ble_svc_hid_chars[HidSvcGattCharacteristicProtocolMode],
|
||||||
|
&hid_svc->chars[HidSvcGattCharacteristicProtocolMode]);
|
||||||
|
|
||||||
|
uint8_t protocol_mode = 1;
|
||||||
|
ble_gatt_characteristic_update(
|
||||||
|
hid_svc->svc_handle,
|
||||||
|
&hid_svc->chars[HidSvcGattCharacteristicProtocolMode],
|
||||||
|
&protocol_mode);
|
||||||
|
|
||||||
|
// reports
|
||||||
|
BleGattCharacteristicDescriptorParams ble_svc_hid_char_descr;
|
||||||
|
BleGattCharacteristicParams report_char;
|
||||||
|
HidSvcReportId report_id;
|
||||||
|
|
||||||
|
memcpy(
|
||||||
|
&ble_svc_hid_char_descr, &ble_svc_hid_char_descr_template, sizeof(ble_svc_hid_char_descr));
|
||||||
|
memcpy(&report_char, &ble_svc_hid_report_template, sizeof(report_char));
|
||||||
|
|
||||||
|
ble_svc_hid_char_descr.data_callback.context = &report_id;
|
||||||
|
report_char.descriptor_params = &ble_svc_hid_char_descr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t report_type;
|
||||||
|
uint8_t report_count;
|
||||||
|
BleGattCharacteristicInstance* chars;
|
||||||
|
} HidSvcReportCharProps;
|
||||||
|
|
||||||
|
HidSvcReportCharProps hid_report_chars[] = {
|
||||||
|
{0x01, BLE_SVC_HID_INPUT_REPORT_COUNT, hid_svc->input_report_chars},
|
||||||
|
{0x02, BLE_SVC_HID_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars},
|
||||||
|
{0x03, BLE_SVC_HID_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars},
|
||||||
|
};
|
||||||
|
|
||||||
|
for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars);
|
||||||
|
report_type_idx++) {
|
||||||
|
report_id.report_type = hid_report_chars[report_type_idx].report_type;
|
||||||
|
for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count;
|
||||||
|
report_idx++) {
|
||||||
|
report_id.report_idx = report_idx + 1;
|
||||||
|
ble_gatt_characteristic_init(
|
||||||
|
hid_svc->svc_handle,
|
||||||
|
&report_char,
|
||||||
|
&hid_report_chars[report_type_idx].chars[report_idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup remaining characteristics
|
||||||
|
for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) {
|
||||||
|
ble_gatt_characteristic_init(
|
||||||
|
hid_svc->svc_handle, &ble_svc_hid_chars[i], &hid_svc->chars[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hid_svc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_svc_hid_update_report_map(BleServiceHid* hid_svc, const uint8_t* data, uint16_t len) {
|
||||||
|
furi_assert(data);
|
||||||
|
furi_assert(hid_svc);
|
||||||
|
|
||||||
|
HidSvcDataWrapper report_data = {
|
||||||
|
.data_ptr = data,
|
||||||
|
.data_len = len,
|
||||||
|
};
|
||||||
|
return ble_gatt_characteristic_update(
|
||||||
|
hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_svc_hid_update_input_report(
|
||||||
|
BleServiceHid* hid_svc,
|
||||||
|
uint8_t input_report_num,
|
||||||
|
uint8_t* data,
|
||||||
|
uint16_t len) {
|
||||||
|
furi_assert(data);
|
||||||
|
furi_assert(hid_svc);
|
||||||
|
furi_assert(input_report_num < BLE_SVC_HID_INPUT_REPORT_COUNT);
|
||||||
|
|
||||||
|
HidSvcDataWrapper report_data = {
|
||||||
|
.data_ptr = data,
|
||||||
|
.data_len = len,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ble_gatt_characteristic_update(
|
||||||
|
hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_svc_hid_update_info(BleServiceHid* hid_svc, uint8_t* data) {
|
||||||
|
furi_assert(data);
|
||||||
|
furi_assert(hid_svc);
|
||||||
|
|
||||||
|
return ble_gatt_characteristic_update(
|
||||||
|
hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_svc_hid_stop(BleServiceHid* hid_svc) {
|
||||||
|
furi_assert(hid_svc);
|
||||||
|
ble_event_dispatcher_unregister_svc_handler(hid_svc->event_handler);
|
||||||
|
// Delete characteristics
|
||||||
|
for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) {
|
||||||
|
ble_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t report_count;
|
||||||
|
BleGattCharacteristicInstance* chars;
|
||||||
|
} HidSvcReportCharProps;
|
||||||
|
|
||||||
|
HidSvcReportCharProps hid_report_chars[] = {
|
||||||
|
{BLE_SVC_HID_INPUT_REPORT_COUNT, hid_svc->input_report_chars},
|
||||||
|
{BLE_SVC_HID_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars},
|
||||||
|
{BLE_SVC_HID_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars},
|
||||||
|
};
|
||||||
|
|
||||||
|
for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars);
|
||||||
|
report_type_idx++) {
|
||||||
|
for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count;
|
||||||
|
report_idx++) {
|
||||||
|
ble_gatt_characteristic_delete(
|
||||||
|
hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete service
|
||||||
|
ble_gatt_service_delete(hid_svc->svc_handle);
|
||||||
|
free(hid_svc);
|
||||||
|
}
|
||||||
31
applications/main/bad_usb/helpers/ble_hid_service.h
Normal file
31
applications/main/bad_usb/helpers/ble_hid_service.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Based on <lib/ble_profile/extra_services/hid_service.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct BleServiceHid BleServiceHid;
|
||||||
|
|
||||||
|
BleServiceHid* ble_svc_hid_start(void);
|
||||||
|
|
||||||
|
void ble_svc_hid_stop(BleServiceHid* service);
|
||||||
|
|
||||||
|
bool ble_svc_hid_update_report_map(BleServiceHid* service, const uint8_t* data, uint16_t len);
|
||||||
|
|
||||||
|
bool ble_svc_hid_update_input_report(
|
||||||
|
BleServiceHid* service,
|
||||||
|
uint8_t input_report_num,
|
||||||
|
uint8_t* data,
|
||||||
|
uint16_t len);
|
||||||
|
|
||||||
|
// Expects data to be of length BLE_SVC_HID_INFO_LEN (4 bytes)
|
||||||
|
bool ble_svc_hid_update_info(BleServiceHid* service, uint8_t* data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -25,6 +25,8 @@ typedef enum {
|
|||||||
} WorkerEvtFlags;
|
} WorkerEvtFlags;
|
||||||
|
|
||||||
static const char ducky_cmd_id[] = {"ID"};
|
static const char ducky_cmd_id[] = {"ID"};
|
||||||
|
static const char ducky_cmd_bt_id[] = {"BT_ID"};
|
||||||
|
static const char ducky_cmd_ble_id[] = {"BLE_ID"};
|
||||||
|
|
||||||
static const uint8_t numpad_keys[10] = {
|
static const uint8_t numpad_keys[10] = {
|
||||||
HID_KEYPAD_0,
|
HID_KEYPAD_0,
|
||||||
@@ -40,11 +42,8 @@ static const uint8_t numpad_keys[10] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uint32_t ducky_get_command_len(const char* line) {
|
uint32_t ducky_get_command_len(const char* line) {
|
||||||
uint32_t len = strlen(line);
|
char* first_space = strchr(line, ' ');
|
||||||
for(uint32_t i = 0; i < len; i++) {
|
return first_space ? (first_space - line) : 0;
|
||||||
if(line[i] == ' ') return i;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ducky_is_line_end(const char chr) {
|
bool ducky_is_line_end(const char chr) {
|
||||||
@@ -180,71 +179,100 @@ static bool ducky_string_next(BadUsbScript* bad_usb) {
|
|||||||
|
|
||||||
static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
||||||
uint32_t line_len = furi_string_size(line);
|
uint32_t line_len = furi_string_size(line);
|
||||||
const char* line_tmp = furi_string_get_cstr(line);
|
const char* line_cstr = furi_string_get_cstr(line);
|
||||||
|
|
||||||
if(line_len == 0) {
|
if(line_len == 0) {
|
||||||
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
||||||
}
|
}
|
||||||
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
|
FURI_LOG_D(WORKER_TAG, "line:%s", line_cstr);
|
||||||
|
|
||||||
// Ducky Lang Functions
|
// Ducky Lang Functions
|
||||||
int32_t cmd_result = ducky_execute_cmd(bad_usb, line_tmp);
|
int32_t cmd_result = ducky_execute_cmd(bad_usb, line_cstr);
|
||||||
if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) {
|
if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) {
|
||||||
return cmd_result;
|
return cmd_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse Keys
|
// Mouse Keys
|
||||||
uint16_t key = ducky_get_mouse_keycode_by_name(line_tmp);
|
uint16_t key = ducky_get_mouse_keycode_by_name(line_cstr);
|
||||||
if(key != HID_MOUSE_INVALID) {
|
if(key != HID_MOUSE_INVALID) {
|
||||||
bad_usb->hid->mouse_press(bad_usb->hid_inst, key);
|
bad_usb->hid->mouse_press(bad_usb->hid_inst, key);
|
||||||
bad_usb->hid->mouse_release(bad_usb->hid_inst, key);
|
bad_usb->hid->mouse_release(bad_usb->hid_inst, key);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special keys + modifiers
|
// Parse chain of modifiers linked by spaces and hyphens
|
||||||
key = ducky_get_keycode(bad_usb, line_tmp, false);
|
uint16_t modifiers = 0;
|
||||||
if(key == HID_KEYBOARD_NONE) {
|
while(1) {
|
||||||
return ducky_error(bad_usb, "No keycode defined for %s", line_tmp);
|
key = ducky_get_next_modifier_keycode_by_name(&line_cstr);
|
||||||
}
|
if(key == HID_KEYBOARD_NONE) break;
|
||||||
if((key & 0xFF00) != 0) {
|
|
||||||
// It's a modifier key
|
modifiers |= key;
|
||||||
uint32_t offset = ducky_get_command_len(line_tmp) + 1;
|
char next_char = *line_cstr;
|
||||||
// ducky_get_command_len() returns 0 without space, so check for != 1
|
if(next_char == ' ' || next_char == '-') line_cstr++;
|
||||||
if(offset != 1 && line_len > offset) {
|
|
||||||
// It's also a key combination
|
|
||||||
key |= ducky_get_keycode(bad_usb, line_tmp + offset, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main key
|
||||||
|
char next_char = *line_cstr;
|
||||||
|
uint16_t main_key = ducky_get_keycode_by_name(line_cstr);
|
||||||
|
if(!main_key && next_char) main_key = BADUSB_ASCII_TO_KEY(bad_usb, next_char);
|
||||||
|
key = modifiers | main_key;
|
||||||
|
|
||||||
|
if(key == 0 && next_char) ducky_error(bad_usb, "No keycode defined for %s", line_cstr);
|
||||||
|
|
||||||
bad_usb->hid->kb_press(bad_usb->hid_inst, key);
|
bad_usb->hid->kb_press(bad_usb->hid_inst, key);
|
||||||
bad_usb->hid->kb_release(bad_usb->hid_inst, key);
|
bad_usb->hid->kb_release(bad_usb->hid_inst, key);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
||||||
if(sscanf(line, "%lX:%lX", &bad_usb->hid_cfg.vid, &bad_usb->hid_cfg.pid) == 2) {
|
FuriHalUsbHidConfig* usb_hid_cfg = &bad_usb->hid_cfg->usb;
|
||||||
bad_usb->hid_cfg.manuf[0] = '\0';
|
|
||||||
bad_usb->hid_cfg.product[0] = '\0';
|
if(sscanf(line, "%lX:%lX", &usb_hid_cfg->vid, &usb_hid_cfg->pid) == 2) {
|
||||||
|
usb_hid_cfg->manuf[0] = '\0';
|
||||||
|
usb_hid_cfg->product[0] = '\0';
|
||||||
|
|
||||||
uint8_t id_len = ducky_get_command_len(line);
|
uint8_t id_len = ducky_get_command_len(line);
|
||||||
if(!ducky_is_line_end(line[id_len + 1])) {
|
if(!ducky_is_line_end(line[id_len + 1])) {
|
||||||
sscanf(
|
sscanf(
|
||||||
&line[id_len + 1],
|
&line[id_len + 1],
|
||||||
"%31[^\r\n:]:%31[^\r\n]",
|
"%31[^\r\n:]:%31[^\r\n]",
|
||||||
bad_usb->hid_cfg.manuf,
|
usb_hid_cfg->manuf,
|
||||||
bad_usb->hid_cfg.product);
|
usb_hid_cfg->product);
|
||||||
}
|
}
|
||||||
FURI_LOG_D(
|
FURI_LOG_D(
|
||||||
WORKER_TAG,
|
WORKER_TAG,
|
||||||
"set id: %04lX:%04lX mfr:%s product:%s",
|
"set id: %04lX:%04lX mfr:%s product:%s",
|
||||||
bad_usb->hid_cfg.vid,
|
usb_hid_cfg->vid,
|
||||||
bad_usb->hid_cfg.pid,
|
usb_hid_cfg->pid,
|
||||||
bad_usb->hid_cfg.manuf,
|
usb_hid_cfg->manuf,
|
||||||
bad_usb->hid_cfg.product);
|
usb_hid_cfg->product);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ducky_set_ble_id(BadUsbScript* bad_usb, const char* line) {
|
||||||
|
BleProfileHidParams* ble_hid_cfg = &bad_usb->hid_cfg->ble;
|
||||||
|
|
||||||
|
size_t line_len = strlen(line);
|
||||||
|
size_t mac_len = sizeof(ble_hid_cfg->mac) * 3; // 2 hex chars + separator per byte
|
||||||
|
if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name
|
||||||
|
|
||||||
|
for(size_t i = 0; i < sizeof(ble_hid_cfg->mac); i++) {
|
||||||
|
const char* hex_byte = &line[i * 3];
|
||||||
|
// This sscanf() doesn't work well with %02hhX, need to use a u32
|
||||||
|
uint32_t temp_uint;
|
||||||
|
if(sscanf(hex_byte, "%02lX", &temp_uint) != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ble_hid_cfg->mac[sizeof(ble_hid_cfg->mac) - 1 - i] = temp_uint;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(ble_hid_cfg->name, line + mac_len, sizeof(ble_hid_cfg->name));
|
||||||
|
FURI_LOG_D(WORKER_TAG, "set ble id: %s", line);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void bad_usb_hid_state_callback(bool state, void* context) {
|
static void bad_usb_hid_state_callback(bool state, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
BadUsbScript* bad_usb = context;
|
BadUsbScript* bad_usb = context;
|
||||||
@@ -283,17 +311,30 @@ static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) {
|
|||||||
}
|
}
|
||||||
} while(ret > 0);
|
} while(ret > 0);
|
||||||
|
|
||||||
|
if(bad_usb->load_id_cfg) {
|
||||||
const char* line_tmp = furi_string_get_cstr(bad_usb->line);
|
const char* line_tmp = furi_string_get_cstr(bad_usb->line);
|
||||||
bool id_set = false; // Looking for ID command at first line
|
BadUsbHidInterface interface = *bad_usb->interface;
|
||||||
|
// Look for ID/BLE_ID/BT_ID command on first line
|
||||||
if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
|
if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
|
||||||
id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]);
|
if(ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1])) {
|
||||||
|
interface = BadUsbHidInterfaceUsb;
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
strncmp(line_tmp, ducky_cmd_ble_id, strlen(ducky_cmd_ble_id)) == 0 ||
|
||||||
|
strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0) {
|
||||||
|
if(ducky_set_ble_id(bad_usb, &line_tmp[ducky_get_command_len(line_tmp) + 1])) {
|
||||||
|
interface = BadUsbHidInterfaceBle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id_set) {
|
// Auto-switch based on ID/BLE_ID/BT_ID command, user can override manually after
|
||||||
bad_usb->hid_inst = bad_usb->hid->init(&bad_usb->hid_cfg);
|
if(interface != *bad_usb->interface) {
|
||||||
} else {
|
*bad_usb->interface = interface;
|
||||||
bad_usb->hid_inst = bad_usb->hid->init(NULL);
|
bad_usb->hid = bad_usb_hid_get_interface(*bad_usb->interface);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bad_usb->hid_inst = bad_usb->hid->init(bad_usb->hid_cfg);
|
||||||
bad_usb->hid->set_state_callback(bad_usb->hid_inst, bad_usb_hid_state_callback, bad_usb);
|
bad_usb->hid->set_state_callback(bad_usb->hid_inst, bad_usb_hid_state_callback, bad_usb);
|
||||||
|
|
||||||
storage_file_seek(script_file, 0, true);
|
storage_file_seek(script_file, 0, true);
|
||||||
@@ -396,9 +437,12 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
bad_usb->line = furi_string_alloc();
|
bad_usb->line = furi_string_alloc();
|
||||||
bad_usb->line_prev = furi_string_alloc();
|
bad_usb->line_prev = furi_string_alloc();
|
||||||
bad_usb->string_print = furi_string_alloc();
|
bad_usb->string_print = furi_string_alloc();
|
||||||
|
bad_usb->st.elapsed = 0;
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
|
uint32_t start = furi_get_tick();
|
||||||
if(worker_state == BadUsbStateInit) { // State: initialization
|
if(worker_state == BadUsbStateInit) { // State: initialization
|
||||||
|
start = 0;
|
||||||
if(storage_file_open(
|
if(storage_file_open(
|
||||||
script_file,
|
script_file,
|
||||||
furi_string_get_cstr(bad_usb->file_path),
|
furi_string_get_cstr(bad_usb->file_path),
|
||||||
@@ -408,7 +452,7 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
if(bad_usb->hid->is_connected(bad_usb->hid_inst)) {
|
if(bad_usb->hid->is_connected(bad_usb->hid_inst)) {
|
||||||
worker_state = BadUsbStateIdle; // Ready to run
|
worker_state = BadUsbStateIdle; // Ready to run
|
||||||
} else {
|
} else {
|
||||||
worker_state = BadUsbStateNotConnected; // USB not connected
|
worker_state = BadUsbStateNotConnected; // Not connected
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
worker_state = BadUsbStateScriptError; // Script preload error
|
worker_state = BadUsbStateScriptError; // Script preload error
|
||||||
@@ -419,7 +463,8 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
|
|
||||||
} else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
|
} else if(worker_state == BadUsbStateNotConnected) { // State: Not connected
|
||||||
|
start = 0;
|
||||||
uint32_t flags = bad_usb_flags_get(
|
uint32_t flags = bad_usb_flags_get(
|
||||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop,
|
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop,
|
||||||
FuriWaitForever);
|
FuriWaitForever);
|
||||||
@@ -429,11 +474,12 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
} else if(flags & WorkerEvtConnect) {
|
} else if(flags & WorkerEvtConnect) {
|
||||||
worker_state = BadUsbStateIdle; // Ready to run
|
worker_state = BadUsbStateIdle; // Ready to run
|
||||||
} else if(flags & WorkerEvtStartStop) {
|
} else if(flags & WorkerEvtStartStop) {
|
||||||
worker_state = BadUsbStateWillRun; // Will run when USB is connected
|
worker_state = BadUsbStateWillRun; // Will run when connected
|
||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
|
|
||||||
} else if(worker_state == BadUsbStateIdle) { // State: ready to start
|
} else if(worker_state == BadUsbStateIdle) { // State: ready to start
|
||||||
|
start = 0;
|
||||||
uint32_t flags = bad_usb_flags_get(
|
uint32_t flags = bad_usb_flags_get(
|
||||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever);
|
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever);
|
||||||
|
|
||||||
@@ -452,12 +498,14 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
bad_usb->file_end = false;
|
bad_usb->file_end = false;
|
||||||
storage_file_seek(script_file, 0, true);
|
storage_file_seek(script_file, 0, true);
|
||||||
worker_state = BadUsbStateRunning;
|
worker_state = BadUsbStateRunning;
|
||||||
|
bad_usb->st.elapsed = 0;
|
||||||
} else if(flags & WorkerEvtDisconnect) {
|
} else if(flags & WorkerEvtDisconnect) {
|
||||||
worker_state = BadUsbStateNotConnected; // USB disconnected
|
worker_state = BadUsbStateNotConnected; // Disconnected
|
||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
|
|
||||||
} else if(worker_state == BadUsbStateWillRun) { // State: start on connection
|
} else if(worker_state == BadUsbStateWillRun) { // State: start on connection
|
||||||
|
start = 0;
|
||||||
uint32_t flags = bad_usb_flags_get(
|
uint32_t flags = bad_usb_flags_get(
|
||||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever);
|
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever);
|
||||||
|
|
||||||
@@ -482,6 +530,7 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
if(flags == (unsigned)FuriFlagErrorTimeout) {
|
if(flags == (unsigned)FuriFlagErrorTimeout) {
|
||||||
// If nothing happened - start script execution
|
// If nothing happened - start script execution
|
||||||
worker_state = BadUsbStateRunning;
|
worker_state = BadUsbStateRunning;
|
||||||
|
bad_usb->st.elapsed = 0;
|
||||||
} else if(flags & WorkerEvtStartStop) {
|
} else if(flags & WorkerEvtStartStop) {
|
||||||
worker_state = BadUsbStateIdle;
|
worker_state = BadUsbStateIdle;
|
||||||
furi_thread_flags_clear(WorkerEvtStartStop);
|
furi_thread_flags_clear(WorkerEvtStartStop);
|
||||||
@@ -492,7 +541,7 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
|
|
||||||
} else if(worker_state == BadUsbStateRunning) { // State: running
|
} else if(worker_state == BadUsbStateRunning) { // State: running
|
||||||
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
uint16_t delay_cur = (delay_val > 100) ? (100) : (delay_val);
|
||||||
uint32_t flags = furi_thread_flags_wait(
|
uint32_t flags = furi_thread_flags_wait(
|
||||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||||
FuriFlagWaitAny,
|
FuriFlagWaitAny,
|
||||||
@@ -506,19 +555,21 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
worker_state = BadUsbStateIdle; // Stop executing script
|
worker_state = BadUsbStateIdle; // Stop executing script
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
} else if(flags & WorkerEvtDisconnect) {
|
} else if(flags & WorkerEvtDisconnect) {
|
||||||
worker_state = BadUsbStateNotConnected; // USB disconnected
|
worker_state = BadUsbStateNotConnected; // Disconnected
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
} else if(flags & WorkerEvtPauseResume) {
|
} else if(flags & WorkerEvtPauseResume) {
|
||||||
pause_state = BadUsbStateRunning;
|
pause_state = BadUsbStateRunning;
|
||||||
worker_state = BadUsbStatePaused; // Pause
|
worker_state = BadUsbStatePaused; // Pause
|
||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
|
bad_usb->st.elapsed += (furi_get_tick() - start);
|
||||||
continue;
|
continue;
|
||||||
} else if(
|
} else if(
|
||||||
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
||||||
(flags == (unsigned)FuriFlagErrorResource)) {
|
(flags == (unsigned)FuriFlagErrorResource)) {
|
||||||
if(delay_val > 0) {
|
if(delay_val > 0) {
|
||||||
bad_usb->st.delay_remain--;
|
bad_usb->st.delay_remain--;
|
||||||
|
bad_usb->st.elapsed += (furi_get_tick() - start);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bad_usb->st.state = BadUsbStateRunning;
|
bad_usb->st.state = BadUsbStateRunning;
|
||||||
@@ -533,6 +584,7 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
worker_state = BadUsbStateIdle;
|
worker_state = BadUsbStateIdle;
|
||||||
bad_usb->st.state = BadUsbStateDone;
|
bad_usb->st.state = BadUsbStateDone;
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
|
bad_usb->st.elapsed += (furi_get_tick() - start);
|
||||||
continue;
|
continue;
|
||||||
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays
|
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays
|
||||||
delay_val = bad_usb->defdelay;
|
delay_val = bad_usb->defdelay;
|
||||||
@@ -541,14 +593,15 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input
|
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input
|
||||||
worker_state = BadUsbStateWaitForBtn;
|
worker_state = BadUsbStateWaitForBtn;
|
||||||
bad_usb->st.state = BadUsbStateWaitForBtn; // Show long delays
|
bad_usb->st.state = BadUsbStateWaitForBtn; // Show long delays
|
||||||
} else if(delay_val > 1000) {
|
} else if(delay_val > 100) {
|
||||||
bad_usb->st.state = BadUsbStateDelay; // Show long delays
|
bad_usb->st.state = BadUsbStateDelay; // Show long delays
|
||||||
bad_usb->st.delay_remain = delay_val / 1000;
|
bad_usb->st.delay_remain = delay_val / 100;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
furi_check((flags & FuriFlagError) == 0);
|
furi_check((flags & FuriFlagError) == 0);
|
||||||
}
|
}
|
||||||
} else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press
|
} else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press
|
||||||
|
start = 0;
|
||||||
uint32_t flags = bad_usb_flags_get(
|
uint32_t flags = bad_usb_flags_get(
|
||||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||||
FuriWaitForever);
|
FuriWaitForever);
|
||||||
@@ -559,13 +612,14 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
delay_val = 0;
|
delay_val = 0;
|
||||||
worker_state = BadUsbStateRunning;
|
worker_state = BadUsbStateRunning;
|
||||||
} else if(flags & WorkerEvtDisconnect) {
|
} else if(flags & WorkerEvtDisconnect) {
|
||||||
worker_state = BadUsbStateNotConnected; // USB disconnected
|
worker_state = BadUsbStateNotConnected; // Disconnected
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if(worker_state == BadUsbStatePaused) { // State: Paused
|
} else if(worker_state == BadUsbStatePaused) { // State: Paused
|
||||||
|
start = 0;
|
||||||
uint32_t flags = bad_usb_flags_get(
|
uint32_t flags = bad_usb_flags_get(
|
||||||
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect,
|
||||||
FuriWaitForever);
|
FuriWaitForever);
|
||||||
@@ -577,14 +631,14 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
} else if(flags & WorkerEvtDisconnect) {
|
} else if(flags & WorkerEvtDisconnect) {
|
||||||
worker_state = BadUsbStateNotConnected; // USB disconnected
|
worker_state = BadUsbStateNotConnected; // Disconnected
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
} else if(flags & WorkerEvtPauseResume) {
|
} else if(flags & WorkerEvtPauseResume) {
|
||||||
if(pause_state == BadUsbStateRunning) {
|
if(pause_state == BadUsbStateRunning) {
|
||||||
if(delay_val > 0) {
|
if(delay_val > 0) {
|
||||||
bad_usb->st.state = BadUsbStateDelay;
|
bad_usb->st.state = BadUsbStateDelay;
|
||||||
bad_usb->st.delay_remain = delay_val / 1000;
|
bad_usb->st.delay_remain = delay_val / 100;
|
||||||
} else {
|
} else {
|
||||||
bad_usb->st.state = BadUsbStateRunning;
|
bad_usb->st.state = BadUsbStateRunning;
|
||||||
delay_val = 0;
|
delay_val = 0;
|
||||||
@@ -611,13 +665,14 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
worker_state = BadUsbStateIdle; // Stop executing script
|
worker_state = BadUsbStateIdle; // Stop executing script
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
} else if(flags & WorkerEvtDisconnect) {
|
} else if(flags & WorkerEvtDisconnect) {
|
||||||
worker_state = BadUsbStateNotConnected; // USB disconnected
|
worker_state = BadUsbStateNotConnected; // Disconnected
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
} else if(flags & WorkerEvtPauseResume) {
|
} else if(flags & WorkerEvtPauseResume) {
|
||||||
pause_state = BadUsbStateStringDelay;
|
pause_state = BadUsbStateStringDelay;
|
||||||
worker_state = BadUsbStatePaused; // Pause
|
worker_state = BadUsbStatePaused; // Pause
|
||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
|
bad_usb->st.elapsed += (furi_get_tick() - start);
|
||||||
continue;
|
continue;
|
||||||
} else if(
|
} else if(
|
||||||
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
||||||
@@ -633,6 +688,7 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
} else if(
|
} else if(
|
||||||
(worker_state == BadUsbStateFileError) ||
|
(worker_state == BadUsbStateFileError) ||
|
||||||
(worker_state == BadUsbStateScriptError)) { // State: error
|
(worker_state == BadUsbStateScriptError)) { // State: error
|
||||||
|
start = 0;
|
||||||
uint32_t flags =
|
uint32_t flags =
|
||||||
bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command
|
bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command
|
||||||
|
|
||||||
@@ -640,6 +696,9 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(start) {
|
||||||
|
bad_usb->st.elapsed += (furi_get_tick() - start);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bad_usb->hid->set_state_callback(bad_usb->hid_inst, NULL, NULL);
|
bad_usb->hid->set_state_callback(bad_usb->hid_inst, NULL, NULL);
|
||||||
@@ -662,7 +721,11 @@ 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)));
|
memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout)));
|
||||||
}
|
}
|
||||||
|
|
||||||
BadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface interface) {
|
BadUsbScript* bad_usb_script_open(
|
||||||
|
FuriString* file_path,
|
||||||
|
BadUsbHidInterface* interface,
|
||||||
|
BadUsbHidConfig* hid_cfg,
|
||||||
|
bool load_id_cfg) {
|
||||||
furi_assert(file_path);
|
furi_assert(file_path);
|
||||||
|
|
||||||
BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
|
BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
|
||||||
@@ -672,7 +735,10 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface inte
|
|||||||
|
|
||||||
bad_usb->st.state = BadUsbStateInit;
|
bad_usb->st.state = BadUsbStateInit;
|
||||||
bad_usb->st.error[0] = '\0';
|
bad_usb->st.error[0] = '\0';
|
||||||
bad_usb->hid = bad_usb_hid_get_interface(interface);
|
bad_usb->interface = interface;
|
||||||
|
bad_usb->hid_cfg = hid_cfg;
|
||||||
|
bad_usb->load_id_cfg = load_id_cfg;
|
||||||
|
bad_usb->hid = bad_usb_hid_get_interface(*bad_usb->interface);
|
||||||
|
|
||||||
bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
|
bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
|
||||||
furi_thread_start(bad_usb->thread);
|
furi_thread_start(bad_usb->thread);
|
||||||
|
|||||||
@@ -30,11 +30,16 @@ typedef struct {
|
|||||||
uint32_t delay_remain;
|
uint32_t delay_remain;
|
||||||
size_t error_line;
|
size_t error_line;
|
||||||
char error[64];
|
char error[64];
|
||||||
|
uint32_t elapsed;
|
||||||
} BadUsbState;
|
} BadUsbState;
|
||||||
|
|
||||||
typedef struct BadUsbScript BadUsbScript;
|
typedef struct BadUsbScript BadUsbScript;
|
||||||
|
|
||||||
BadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface interface);
|
BadUsbScript* bad_usb_script_open(
|
||||||
|
FuriString* file_path,
|
||||||
|
BadUsbHidInterface* interface,
|
||||||
|
BadUsbHidConfig* hid_cfg,
|
||||||
|
bool load_id_cfg);
|
||||||
|
|
||||||
void bad_usb_script_close(BadUsbScript* bad_usb);
|
void bad_usb_script_close(BadUsbScript* bad_usb);
|
||||||
|
|
||||||
|
|||||||
@@ -256,6 +256,8 @@ static int32_t ducky_fnc_mouse_move(BadUsbScript* bad_usb, const char* line, int
|
|||||||
static const DuckyCmd ducky_commands[] = {
|
static const DuckyCmd ducky_commands[] = {
|
||||||
{"REM", NULL, -1},
|
{"REM", NULL, -1},
|
||||||
{"ID", NULL, -1},
|
{"ID", NULL, -1},
|
||||||
|
{"BT_ID", NULL, -1},
|
||||||
|
{"BLE_ID", NULL, -1},
|
||||||
{"DELAY", ducky_fnc_delay, -1},
|
{"DELAY", ducky_fnc_delay, -1},
|
||||||
{"STRING", ducky_fnc_string, 0},
|
{"STRING", ducky_fnc_string, 0},
|
||||||
{"STRINGLN", ducky_fnc_string, 1},
|
{"STRINGLN", ducky_fnc_string, 1},
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ extern "C" {
|
|||||||
#define HID_MOUSE_NONE 0
|
#define HID_MOUSE_NONE 0
|
||||||
|
|
||||||
struct BadUsbScript {
|
struct BadUsbScript {
|
||||||
FuriHalUsbHidConfig hid_cfg;
|
BadUsbHidInterface* interface;
|
||||||
|
BadUsbHidConfig* hid_cfg;
|
||||||
|
bool load_id_cfg;
|
||||||
const BadUsbHidApi* hid;
|
const BadUsbHidApi* hid;
|
||||||
void* hid_inst;
|
void* hid_inst;
|
||||||
FuriThread* thread;
|
FuriThread* thread;
|
||||||
@@ -54,6 +56,8 @@ uint32_t ducky_get_command_len(const char* line);
|
|||||||
|
|
||||||
bool ducky_is_line_end(const char chr);
|
bool ducky_is_line_end(const char chr);
|
||||||
|
|
||||||
|
uint16_t ducky_get_next_modifier_keycode_by_name(const char** param);
|
||||||
|
|
||||||
uint16_t ducky_get_keycode_by_name(const char* param);
|
uint16_t ducky_get_keycode_by_name(const char* param);
|
||||||
|
|
||||||
uint16_t ducky_get_media_keycode_by_name(const char* param);
|
uint16_t ducky_get_media_keycode_by_name(const char* param);
|
||||||
|
|||||||
@@ -6,21 +6,16 @@ typedef struct {
|
|||||||
uint16_t keycode;
|
uint16_t keycode;
|
||||||
} DuckyKey;
|
} DuckyKey;
|
||||||
|
|
||||||
static const DuckyKey ducky_keys[] = {
|
static const DuckyKey ducky_modifier_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},
|
{"CTRL", KEY_MOD_LEFT_CTRL},
|
||||||
{"CONTROL", KEY_MOD_LEFT_CTRL},
|
{"CONTROL", KEY_MOD_LEFT_CTRL},
|
||||||
{"SHIFT", KEY_MOD_LEFT_SHIFT},
|
{"SHIFT", KEY_MOD_LEFT_SHIFT},
|
||||||
{"ALT", KEY_MOD_LEFT_ALT},
|
{"ALT", KEY_MOD_LEFT_ALT},
|
||||||
{"GUI", KEY_MOD_LEFT_GUI},
|
{"GUI", KEY_MOD_LEFT_GUI},
|
||||||
{"WINDOWS", KEY_MOD_LEFT_GUI},
|
{"WINDOWS", KEY_MOD_LEFT_GUI},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DuckyKey ducky_keys[] = {
|
||||||
{"DOWNARROW", HID_KEYBOARD_DOWN_ARROW},
|
{"DOWNARROW", HID_KEYBOARD_DOWN_ARROW},
|
||||||
{"DOWN", HID_KEYBOARD_DOWN_ARROW},
|
{"DOWN", HID_KEYBOARD_DOWN_ARROW},
|
||||||
{"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
|
{"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
|
||||||
@@ -119,6 +114,23 @@ static const DuckyKey ducky_mouse_keys[] = {
|
|||||||
{"WHEEL_CLICK", HID_MOUSE_BTN_WHEEL},
|
{"WHEEL_CLICK", HID_MOUSE_BTN_WHEEL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint16_t ducky_get_next_modifier_keycode_by_name(const char** param) {
|
||||||
|
const char* input_str = *param;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < COUNT_OF(ducky_modifier_keys); i++) {
|
||||||
|
size_t key_cmd_len = strlen(ducky_modifier_keys[i].name);
|
||||||
|
if((strncmp(input_str, ducky_modifier_keys[i].name, key_cmd_len) == 0)) {
|
||||||
|
char next_char_after_key = input_str[key_cmd_len];
|
||||||
|
if(ducky_is_line_end(next_char_after_key) || (next_char_after_key == '-')) {
|
||||||
|
*param = &input_str[key_cmd_len];
|
||||||
|
return ducky_modifier_keys[i].keycode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return HID_KEYBOARD_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t ducky_get_keycode_by_name(const char* param) {
|
uint16_t ducky_get_keycode_by_name(const char* param) {
|
||||||
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
|
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
|
||||||
size_t key_cmd_len = strlen(ducky_keys[i].name);
|
size_t key_cmd_len = strlen(ducky_keys[i].name);
|
||||||
|
|||||||
BIN
applications/main/bad_usb/resources/badusb/assets/layouts/de-DE-mac.kl
Executable file
BIN
applications/main/bad_usb/resources/badusb/assets/layouts/de-DE-mac.kl
Executable file
Binary file not shown.
@@ -1,10 +1,68 @@
|
|||||||
#include "../bad_usb_app_i.h"
|
#include "../bad_usb_app_i.h"
|
||||||
|
|
||||||
enum SubmenuIndex {
|
enum ConfigIndex {
|
||||||
ConfigIndexKeyboardLayout,
|
ConfigIndexKeyboardLayout,
|
||||||
ConfigIndexBleUnpair,
|
ConfigIndexConnection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ConfigIndexBle {
|
||||||
|
ConfigIndexBlePersistPairing = ConfigIndexConnection + 1,
|
||||||
|
ConfigIndexBlePairingMode,
|
||||||
|
ConfigIndexBleSetDeviceName,
|
||||||
|
ConfigIndexBleSetMacAddress,
|
||||||
|
ConfigIndexBleRandomizeMacAddress,
|
||||||
|
ConfigIndexBleRestoreDefaults,
|
||||||
|
ConfigIndexBleRemovePairing,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ConfigIndexUsb {
|
||||||
|
ConfigIndexUsbSetManufacturerName = ConfigIndexConnection + 1,
|
||||||
|
ConfigIndexUsbSetProductName,
|
||||||
|
ConfigIndexUsbSetVidPid,
|
||||||
|
ConfigIndexUsbRandomizeVidPid,
|
||||||
|
ConfigIndexUsbRestoreDefaults,
|
||||||
|
};
|
||||||
|
|
||||||
|
void bad_usb_scene_config_connection_callback(VariableItem* item) {
|
||||||
|
BadUsbApp* bad_usb = variable_item_get_context(item);
|
||||||
|
bad_usb_set_interface(
|
||||||
|
bad_usb,
|
||||||
|
bad_usb->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb :
|
||||||
|
BadUsbHidInterfaceBle);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, bad_usb->interface == BadUsbHidInterfaceBle ? "BLE" : "USB");
|
||||||
|
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, ConfigIndexConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_ble_persist_pairing_callback(VariableItem* item) {
|
||||||
|
BadUsbApp* bad_usb = variable_item_get_context(item);
|
||||||
|
bool value = variable_item_get_current_value_index(item);
|
||||||
|
const BadUsbHidApi* hid = bad_usb_hid_get_interface(bad_usb->interface);
|
||||||
|
// Apply to current script config
|
||||||
|
bad_usb->script_hid_cfg.ble.bonding = value;
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
bad_usb->user_hid_cfg.ble.bonding = value;
|
||||||
|
variable_item_set_current_value_text(item, value ? "ON" : "OFF");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* const ble_pairing_mode_names[GapPairingCount] = {
|
||||||
|
"YesNo",
|
||||||
|
"PIN Type",
|
||||||
|
"PIN Y/N",
|
||||||
|
};
|
||||||
|
void bad_usb_scene_config_ble_pairing_mode_callback(VariableItem* item) {
|
||||||
|
BadUsbApp* bad_usb = variable_item_get_context(item);
|
||||||
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
const BadUsbHidApi* hid = bad_usb_hid_get_interface(bad_usb->interface);
|
||||||
|
// Apply to current script config
|
||||||
|
bad_usb->script_hid_cfg.ble.pairing = index;
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
bad_usb->user_hid_cfg.ble.pairing = index;
|
||||||
|
variable_item_set_current_value_text(item, ble_pairing_mode_names[index]);
|
||||||
|
}
|
||||||
|
|
||||||
void bad_usb_scene_config_select_callback(void* context, uint32_t index) {
|
void bad_usb_scene_config_select_callback(void* context, uint32_t index) {
|
||||||
BadUsbApp* bad_usb = context;
|
BadUsbApp* bad_usb = context;
|
||||||
|
|
||||||
@@ -13,12 +71,59 @@ void bad_usb_scene_config_select_callback(void* context, uint32_t index) {
|
|||||||
|
|
||||||
static void draw_menu(BadUsbApp* bad_usb) {
|
static void draw_menu(BadUsbApp* bad_usb) {
|
||||||
VariableItemList* var_item_list = bad_usb->var_item_list;
|
VariableItemList* var_item_list = bad_usb->var_item_list;
|
||||||
|
VariableItem* item;
|
||||||
|
|
||||||
variable_item_list_reset(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, "Keyboard Layout (global)", 0, NULL, NULL);
|
||||||
|
|
||||||
variable_item_list_add(var_item_list, "Remove Pairing", 0, NULL, NULL);
|
item = variable_item_list_add(
|
||||||
|
var_item_list, "Connection", 2, bad_usb_scene_config_connection_callback, bad_usb);
|
||||||
|
variable_item_set_current_value_index(item, bad_usb->interface == BadUsbHidInterfaceBle);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, bad_usb->interface == BadUsbHidInterfaceBle ? "BLE" : "USB");
|
||||||
|
|
||||||
|
if(bad_usb->interface == BadUsbHidInterfaceBle) {
|
||||||
|
BleProfileHidParams* ble_hid_cfg = &bad_usb->script_hid_cfg.ble;
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
var_item_list,
|
||||||
|
"Persist Pairing",
|
||||||
|
2,
|
||||||
|
bad_usb_scene_config_ble_persist_pairing_callback,
|
||||||
|
bad_usb);
|
||||||
|
variable_item_set_current_value_index(item, ble_hid_cfg->bonding);
|
||||||
|
variable_item_set_current_value_text(item, ble_hid_cfg->bonding ? "ON" : "OFF");
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
var_item_list,
|
||||||
|
"Pairing Mode",
|
||||||
|
GapPairingCount,
|
||||||
|
bad_usb_scene_config_ble_pairing_mode_callback,
|
||||||
|
bad_usb);
|
||||||
|
variable_item_set_current_value_index(item, ble_hid_cfg->pairing);
|
||||||
|
variable_item_set_current_value_text(item, ble_pairing_mode_names[ble_hid_cfg->pairing]);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Set Device Name", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Set MAC Address", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Randomize MAC Address", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Restore BLE Defaults", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Remove BLE Pairing", 0, NULL, NULL);
|
||||||
|
} else {
|
||||||
|
variable_item_list_add(var_item_list, "Set Manufacturer Name", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Set Product Name", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Set VID and PID", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Randomize VID and PID", 0, NULL, NULL);
|
||||||
|
|
||||||
|
variable_item_list_add(var_item_list, "Restore USB Defaults", 0, NULL, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bad_usb_scene_config_on_enter(void* context) {
|
void bad_usb_scene_config_on_enter(void* context) {
|
||||||
@@ -28,7 +133,8 @@ void bad_usb_scene_config_on_enter(void* context) {
|
|||||||
variable_item_list_set_enter_callback(
|
variable_item_list_set_enter_callback(
|
||||||
var_item_list, bad_usb_scene_config_select_callback, bad_usb);
|
var_item_list, bad_usb_scene_config_select_callback, bad_usb);
|
||||||
draw_menu(bad_usb);
|
draw_menu(bad_usb);
|
||||||
variable_item_list_set_selected_item(var_item_list, 0);
|
variable_item_list_set_selected_item(
|
||||||
|
var_item_list, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig));
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig);
|
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig);
|
||||||
}
|
}
|
||||||
@@ -38,13 +144,110 @@ bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) {
|
|||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
if(event.event == ConfigIndexKeyboardLayout) {
|
const BadUsbHidApi* hid = bad_usb_hid_get_interface(bad_usb->interface);
|
||||||
|
|
||||||
|
switch(event.event) {
|
||||||
|
case ConfigIndexKeyboardLayout:
|
||||||
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout);
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout);
|
||||||
} else if(event.event == ConfigIndexBleUnpair) {
|
break;
|
||||||
|
case ConfigIndexConnection:
|
||||||
|
// Refresh default values for new interface
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Redraw menu with new interface options
|
||||||
|
draw_menu(bad_usb);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(bad_usb->interface == BadUsbHidInterfaceBle) {
|
||||||
|
switch(event.event) {
|
||||||
|
case ConfigIndexBleSetDeviceName:
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBleName);
|
||||||
|
break;
|
||||||
|
case ConfigIndexBleSetMacAddress:
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBleMac);
|
||||||
|
break;
|
||||||
|
case ConfigIndexBleRandomizeMacAddress:
|
||||||
|
// Apply to current script config
|
||||||
|
furi_hal_random_fill_buf(
|
||||||
|
bad_usb->script_hid_cfg.ble.mac, sizeof(bad_usb->script_hid_cfg.ble.mac));
|
||||||
|
bad_usb->script_hid_cfg.ble.mac[sizeof(bad_usb->script_hid_cfg.ble.mac) - 1] |=
|
||||||
|
0b11 << 6; // Set 2 MSB for Random Static Address
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
memcpy(
|
||||||
|
bad_usb->user_hid_cfg.ble.mac,
|
||||||
|
bad_usb->script_hid_cfg.ble.mac,
|
||||||
|
sizeof(bad_usb->user_hid_cfg.ble.mac));
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneDone);
|
||||||
|
break;
|
||||||
|
case ConfigIndexBleRestoreDefaults:
|
||||||
|
// Apply to current script config
|
||||||
|
bad_usb->script_hid_cfg.ble.name[0] = '\0';
|
||||||
|
memset(
|
||||||
|
bad_usb->script_hid_cfg.ble.mac, 0, sizeof(bad_usb->script_hid_cfg.ble.mac));
|
||||||
|
bad_usb->script_hid_cfg.ble.bonding = true;
|
||||||
|
bad_usb->script_hid_cfg.ble.pairing = GapPairingPinCodeVerifyYesNo;
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
memcpy(
|
||||||
|
&bad_usb->user_hid_cfg.ble,
|
||||||
|
&bad_usb->script_hid_cfg.ble,
|
||||||
|
sizeof(bad_usb->user_hid_cfg.ble));
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneDone);
|
||||||
|
break;
|
||||||
|
case ConfigIndexBleRemovePairing:
|
||||||
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfirmUnpair);
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfirmUnpair);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
furi_crash("Unknown key type");
|
switch(event.event) {
|
||||||
|
case ConfigIndexUsbSetManufacturerName:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
bad_usb->scene_manager, BadUsbSceneConfigUsbName, true);
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsbName);
|
||||||
|
break;
|
||||||
|
case ConfigIndexUsbSetProductName:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
bad_usb->scene_manager, BadUsbSceneConfigUsbName, false);
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsbName);
|
||||||
|
break;
|
||||||
|
case ConfigIndexUsbSetVidPid:
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsbVidPid);
|
||||||
|
break;
|
||||||
|
case ConfigIndexUsbRandomizeVidPid:
|
||||||
|
furi_hal_random_fill_buf(
|
||||||
|
(void*)bad_usb->usb_vidpid_buf, sizeof(bad_usb->usb_vidpid_buf));
|
||||||
|
// Apply to current script config
|
||||||
|
bad_usb->script_hid_cfg.usb.vid = bad_usb->usb_vidpid_buf[0];
|
||||||
|
bad_usb->script_hid_cfg.usb.pid = bad_usb->usb_vidpid_buf[1];
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
bad_usb->user_hid_cfg.usb.vid = bad_usb->script_hid_cfg.usb.vid;
|
||||||
|
bad_usb->user_hid_cfg.usb.pid = bad_usb->script_hid_cfg.usb.pid;
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneDone);
|
||||||
|
break;
|
||||||
|
case ConfigIndexUsbRestoreDefaults:
|
||||||
|
// Apply to current script config
|
||||||
|
bad_usb->script_hid_cfg.usb.vid = 0;
|
||||||
|
bad_usb->script_hid_cfg.usb.pid = 0;
|
||||||
|
bad_usb->script_hid_cfg.usb.manuf[0] = '\0';
|
||||||
|
bad_usb->script_hid_cfg.usb.product[0] = '\0';
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
memcpy(
|
||||||
|
&bad_usb->user_hid_cfg.usb,
|
||||||
|
&bad_usb->script_hid_cfg.usb,
|
||||||
|
sizeof(bad_usb->user_hid_cfg.usb));
|
||||||
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneDone);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,9 @@ ADD_SCENE(bad_usb, work, Work)
|
|||||||
ADD_SCENE(bad_usb, error, Error)
|
ADD_SCENE(bad_usb, error, Error)
|
||||||
ADD_SCENE(bad_usb, config, Config)
|
ADD_SCENE(bad_usb, config, Config)
|
||||||
ADD_SCENE(bad_usb, config_layout, ConfigLayout)
|
ADD_SCENE(bad_usb, config_layout, ConfigLayout)
|
||||||
|
ADD_SCENE(bad_usb, config_ble_name, ConfigBleName)
|
||||||
|
ADD_SCENE(bad_usb, config_ble_mac, ConfigBleMac)
|
||||||
|
ADD_SCENE(bad_usb, config_usb_name, ConfigUsbName)
|
||||||
|
ADD_SCENE(bad_usb, config_usb_vidpid, ConfigUsbVidPid)
|
||||||
ADD_SCENE(bad_usb, confirm_unpair, ConfirmUnpair)
|
ADD_SCENE(bad_usb, confirm_unpair, ConfirmUnpair)
|
||||||
ADD_SCENE(bad_usb, unpair_done, UnpairDone)
|
ADD_SCENE(bad_usb, done, Done)
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#include "../bad_usb_app_i.h"
|
||||||
|
|
||||||
|
enum ByteInputResult {
|
||||||
|
ByteInputResultOk,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) {
|
||||||
|
uint8_t tmp;
|
||||||
|
for(size_t i = 0; i < GAP_MAC_ADDR_SIZE / 2; i++) {
|
||||||
|
tmp = mac_addr[i];
|
||||||
|
mac_addr[i] = mac_addr[GAP_MAC_ADDR_SIZE - 1 - i];
|
||||||
|
mac_addr[GAP_MAC_ADDR_SIZE - 1 - i] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_ble_mac_byte_input_callback(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, ByteInputResultOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_ble_mac_on_enter(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
ByteInput* byte_input = bad_usb->byte_input;
|
||||||
|
|
||||||
|
memcpy(bad_usb->ble_mac_buf, bad_usb->script_hid_cfg.ble.mac, sizeof(bad_usb->ble_mac_buf));
|
||||||
|
reverse_mac_addr(bad_usb->ble_mac_buf);
|
||||||
|
byte_input_set_header_text(byte_input, "Set BLE MAC address");
|
||||||
|
|
||||||
|
byte_input_set_result_callback(
|
||||||
|
byte_input,
|
||||||
|
bad_usb_scene_config_ble_mac_byte_input_callback,
|
||||||
|
NULL,
|
||||||
|
bad_usb,
|
||||||
|
bad_usb->ble_mac_buf,
|
||||||
|
sizeof(bad_usb->ble_mac_buf));
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewByteInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bad_usb_scene_config_ble_mac_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
consumed = true;
|
||||||
|
if(event.event == ByteInputResultOk) {
|
||||||
|
const BadUsbHidApi* hid = bad_usb_hid_get_interface(bad_usb->interface);
|
||||||
|
reverse_mac_addr(bad_usb->ble_mac_buf);
|
||||||
|
// Apply to current script config
|
||||||
|
memcpy(
|
||||||
|
bad_usb->script_hid_cfg.ble.mac,
|
||||||
|
bad_usb->ble_mac_buf,
|
||||||
|
sizeof(bad_usb->script_hid_cfg.ble.mac));
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
memcpy(
|
||||||
|
bad_usb->user_hid_cfg.ble.mac,
|
||||||
|
bad_usb->ble_mac_buf,
|
||||||
|
sizeof(bad_usb->user_hid_cfg.ble.mac));
|
||||||
|
}
|
||||||
|
scene_manager_previous_scene(bad_usb->scene_manager);
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_ble_mac_on_exit(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
ByteInput* byte_input = bad_usb->byte_input;
|
||||||
|
|
||||||
|
byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0);
|
||||||
|
byte_input_set_header_text(byte_input, "");
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#include "../bad_usb_app_i.h"
|
||||||
|
|
||||||
|
enum TextInputResult {
|
||||||
|
TextInputResultOk,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bad_usb_scene_config_ble_name_text_input_callback(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, TextInputResultOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_ble_name_on_enter(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
TextInput* text_input = bad_usb->text_input;
|
||||||
|
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->ble_name_buf, bad_usb->script_hid_cfg.ble.name, sizeof(bad_usb->ble_name_buf));
|
||||||
|
text_input_set_header_text(text_input, "Set BLE device name");
|
||||||
|
|
||||||
|
text_input_set_result_callback(
|
||||||
|
text_input,
|
||||||
|
bad_usb_scene_config_ble_name_text_input_callback,
|
||||||
|
bad_usb,
|
||||||
|
bad_usb->ble_name_buf,
|
||||||
|
sizeof(bad_usb->ble_name_buf),
|
||||||
|
true);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewTextInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bad_usb_scene_config_ble_name_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
consumed = true;
|
||||||
|
if(event.event == TextInputResultOk) {
|
||||||
|
const BadUsbHidApi* hid = bad_usb_hid_get_interface(bad_usb->interface);
|
||||||
|
// Apply to current script config
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->script_hid_cfg.ble.name,
|
||||||
|
bad_usb->ble_name_buf,
|
||||||
|
sizeof(bad_usb->script_hid_cfg.ble.name));
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->user_hid_cfg.ble.name,
|
||||||
|
bad_usb->ble_name_buf,
|
||||||
|
sizeof(bad_usb->user_hid_cfg.ble.name));
|
||||||
|
}
|
||||||
|
scene_manager_previous_scene(bad_usb->scene_manager);
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_ble_name_on_exit(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
TextInput* text_input = bad_usb->text_input;
|
||||||
|
|
||||||
|
text_input_reset(text_input);
|
||||||
|
}
|
||||||
@@ -29,21 +29,17 @@ static bool bad_usb_layout_select(BadUsbApp* bad_usb) {
|
|||||||
void bad_usb_scene_config_layout_on_enter(void* context) {
|
void bad_usb_scene_config_layout_on_enter(void* context) {
|
||||||
BadUsbApp* bad_usb = context;
|
BadUsbApp* bad_usb = context;
|
||||||
|
|
||||||
if(bad_usb_layout_select(bad_usb)) {
|
bad_usb_layout_select(bad_usb);
|
||||||
scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneWork);
|
|
||||||
} else {
|
|
||||||
scene_manager_previous_scene(bad_usb->scene_manager);
|
scene_manager_previous_scene(bad_usb->scene_manager);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
|
bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
UNUSED(event);
|
UNUSED(event);
|
||||||
// BadUsbApp* bad_usb = context;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bad_usb_scene_config_layout_on_exit(void* context) {
|
void bad_usb_scene_config_layout_on_exit(void* context) {
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
// BadUsbApp* bad_usb = context;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#include "../bad_usb_app_i.h"
|
||||||
|
|
||||||
|
enum TextInputResult {
|
||||||
|
TextInputResultOk,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bad_usb_scene_config_usb_name_text_input_callback(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, TextInputResultOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_usb_name_on_enter(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
TextInput* text_input = bad_usb->text_input;
|
||||||
|
|
||||||
|
if(scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfigUsbName)) {
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->usb_name_buf,
|
||||||
|
bad_usb->script_hid_cfg.usb.manuf,
|
||||||
|
sizeof(bad_usb->usb_name_buf));
|
||||||
|
text_input_set_header_text(text_input, "Set USB manufacturer name");
|
||||||
|
} else {
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->usb_name_buf,
|
||||||
|
bad_usb->script_hid_cfg.usb.product,
|
||||||
|
sizeof(bad_usb->usb_name_buf));
|
||||||
|
text_input_set_header_text(text_input, "Set USB product name");
|
||||||
|
}
|
||||||
|
|
||||||
|
text_input_set_result_callback(
|
||||||
|
text_input,
|
||||||
|
bad_usb_scene_config_usb_name_text_input_callback,
|
||||||
|
bad_usb,
|
||||||
|
bad_usb->usb_name_buf,
|
||||||
|
sizeof(bad_usb->usb_name_buf),
|
||||||
|
true);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewTextInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bad_usb_scene_config_usb_name_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
consumed = true;
|
||||||
|
if(event.event == TextInputResultOk) {
|
||||||
|
const BadUsbHidApi* hid = bad_usb_hid_get_interface(bad_usb->interface);
|
||||||
|
if(scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfigUsbName)) {
|
||||||
|
// Apply to current script config
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->script_hid_cfg.usb.manuf,
|
||||||
|
bad_usb->usb_name_buf,
|
||||||
|
sizeof(bad_usb->script_hid_cfg.usb.manuf));
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->user_hid_cfg.usb.manuf,
|
||||||
|
bad_usb->usb_name_buf,
|
||||||
|
sizeof(bad_usb->user_hid_cfg.usb.manuf));
|
||||||
|
} else {
|
||||||
|
// Apply to current script config
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->script_hid_cfg.usb.product,
|
||||||
|
bad_usb->usb_name_buf,
|
||||||
|
sizeof(bad_usb->script_hid_cfg.usb.product));
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
strlcpy(
|
||||||
|
bad_usb->user_hid_cfg.usb.product,
|
||||||
|
bad_usb->usb_name_buf,
|
||||||
|
sizeof(bad_usb->user_hid_cfg.usb.product));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scene_manager_previous_scene(bad_usb->scene_manager);
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_usb_name_on_exit(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
TextInput* text_input = bad_usb->text_input;
|
||||||
|
|
||||||
|
text_input_reset(text_input);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#include "../bad_usb_app_i.h"
|
||||||
|
|
||||||
|
enum ByteInputResult {
|
||||||
|
ByteInputResultOk,
|
||||||
|
};
|
||||||
|
|
||||||
|
void bad_usb_scene_config_usb_vidpid_byte_input_callback(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, ByteInputResultOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_usb_vidpid_on_enter(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
ByteInput* byte_input = bad_usb->byte_input;
|
||||||
|
|
||||||
|
bad_usb->usb_vidpid_buf[0] = __builtin_bswap16(bad_usb->script_hid_cfg.usb.vid);
|
||||||
|
bad_usb->usb_vidpid_buf[1] = __builtin_bswap16(bad_usb->script_hid_cfg.usb.pid);
|
||||||
|
byte_input_set_header_text(byte_input, "Set USB VID:PID");
|
||||||
|
|
||||||
|
byte_input_set_result_callback(
|
||||||
|
byte_input,
|
||||||
|
bad_usb_scene_config_usb_vidpid_byte_input_callback,
|
||||||
|
NULL,
|
||||||
|
bad_usb,
|
||||||
|
(void*)bad_usb->usb_vidpid_buf,
|
||||||
|
sizeof(bad_usb->usb_vidpid_buf));
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewByteInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bad_usb_scene_config_usb_vidpid_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
consumed = true;
|
||||||
|
if(event.event == ByteInputResultOk) {
|
||||||
|
const BadUsbHidApi* hid = bad_usb_hid_get_interface(bad_usb->interface);
|
||||||
|
// Apply to current script config
|
||||||
|
bad_usb->script_hid_cfg.usb.vid = __builtin_bswap16(bad_usb->usb_vidpid_buf[0]);
|
||||||
|
bad_usb->script_hid_cfg.usb.pid = __builtin_bswap16(bad_usb->usb_vidpid_buf[1]);
|
||||||
|
hid->adjust_config(&bad_usb->script_hid_cfg);
|
||||||
|
// Set in user config to save in settings file
|
||||||
|
bad_usb->user_hid_cfg.usb.vid = bad_usb->script_hid_cfg.usb.vid;
|
||||||
|
bad_usb->user_hid_cfg.usb.pid = bad_usb->script_hid_cfg.usb.pid;
|
||||||
|
}
|
||||||
|
scene_manager_previous_scene(bad_usb->scene_manager);
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad_usb_scene_config_usb_vidpid_on_exit(void* context) {
|
||||||
|
BadUsbApp* bad_usb = context;
|
||||||
|
ByteInput* byte_input = bad_usb->byte_input;
|
||||||
|
|
||||||
|
byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0);
|
||||||
|
byte_input_set_header_text(byte_input, "");
|
||||||
|
}
|
||||||
@@ -36,7 +36,8 @@ bool bad_usb_scene_confirm_unpair_on_event(void* context, SceneManagerEvent even
|
|||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
consumed = true;
|
consumed = true;
|
||||||
if(event.event == GuiButtonTypeRight) {
|
if(event.event == GuiButtonTypeRight) {
|
||||||
scene_manager_next_scene(scene_manager, BadUsbSceneUnpairDone);
|
bad_usb_hid_ble_remove_pairing();
|
||||||
|
scene_manager_next_scene(scene_manager, BadUsbSceneDone);
|
||||||
} else if(event.event == GuiButtonTypeLeft) {
|
} else if(event.event == GuiButtonTypeLeft) {
|
||||||
scene_manager_previous_scene(scene_manager);
|
scene_manager_previous_scene(scene_manager);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
#include "../bad_usb_app_i.h"
|
#include "../bad_usb_app_i.h"
|
||||||
|
|
||||||
static void bad_usb_scene_unpair_done_popup_callback(void* context) {
|
static void bad_usb_scene_done_popup_callback(void* context) {
|
||||||
BadUsbApp* bad_usb = context;
|
BadUsbApp* bad_usb = context;
|
||||||
scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneConfig);
|
scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bad_usb_scene_unpair_done_on_enter(void* context) {
|
void bad_usb_scene_done_on_enter(void* context) {
|
||||||
BadUsbApp* bad_usb = context;
|
BadUsbApp* bad_usb = context;
|
||||||
Popup* popup = bad_usb->popup;
|
Popup* popup = bad_usb->popup;
|
||||||
|
|
||||||
bad_usb_hid_ble_remove_pairing();
|
|
||||||
|
|
||||||
popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58);
|
popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58);
|
||||||
popup_set_header(popup, "Done", 20, 19, AlignLeft, AlignBottom);
|
popup_set_header(popup, "Done", 20, 19, AlignLeft, AlignBottom);
|
||||||
popup_set_callback(popup, bad_usb_scene_unpair_done_popup_callback);
|
popup_set_callback(popup, bad_usb_scene_done_popup_callback);
|
||||||
popup_set_context(popup, bad_usb);
|
popup_set_context(popup, bad_usb);
|
||||||
popup_set_timeout(popup, 1500);
|
popup_set_timeout(popup, 1500);
|
||||||
popup_enable_timeout(popup);
|
popup_enable_timeout(popup);
|
||||||
@@ -21,7 +19,7 @@ void bad_usb_scene_unpair_done_on_enter(void* context) {
|
|||||||
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewPopup);
|
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewPopup);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bad_usb_scene_unpair_done_on_event(void* context, SceneManagerEvent event) {
|
bool bad_usb_scene_done_on_event(void* context, SceneManagerEvent event) {
|
||||||
BadUsbApp* bad_usb = context;
|
BadUsbApp* bad_usb = context;
|
||||||
UNUSED(bad_usb);
|
UNUSED(bad_usb);
|
||||||
UNUSED(event);
|
UNUSED(event);
|
||||||
@@ -30,7 +28,7 @@ bool bad_usb_scene_unpair_done_on_event(void* context, SceneManagerEvent event)
|
|||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bad_usb_scene_unpair_done_on_exit(void* context) {
|
void bad_usb_scene_done_on_exit(void* context) {
|
||||||
BadUsbApp* bad_usb = context;
|
BadUsbApp* bad_usb = context;
|
||||||
Popup* popup = bad_usb->popup;
|
Popup* popup = bad_usb->popup;
|
||||||
UNUSED(popup);
|
UNUSED(popup);
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "../bad_usb_app_i.h"
|
#include "../bad_usb_app_i.h"
|
||||||
#include <furi_hal_power.h>
|
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
|
|
||||||
static bool bad_usb_file_select(BadUsbApp* bad_usb) {
|
static bool bad_usb_file_select(BadUsbApp* bad_usb) {
|
||||||
@@ -27,6 +26,7 @@ void bad_usb_scene_file_select_on_enter(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(bad_usb_file_select(bad_usb)) {
|
if(bad_usb_file_select(bad_usb)) {
|
||||||
|
scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneWork, true);
|
||||||
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork);
|
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork);
|
||||||
} else {
|
} else {
|
||||||
view_dispatcher_stop(bad_usb->view_dispatcher);
|
view_dispatcher_stop(bad_usb->view_dispatcher);
|
||||||
@@ -36,11 +36,9 @@ void bad_usb_scene_file_select_on_enter(void* context) {
|
|||||||
bool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) {
|
bool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) {
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
UNUSED(event);
|
UNUSED(event);
|
||||||
// BadUsbApp* bad_usb = context;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bad_usb_scene_file_select_on_exit(void* context) {
|
void bad_usb_scene_file_select_on_exit(void* context) {
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
// BadUsbApp* bad_usb = context;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,8 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
|
|||||||
bad_usb_script_close(app->bad_usb_script);
|
bad_usb_script_close(app->bad_usb_script);
|
||||||
app->bad_usb_script = NULL;
|
app->bad_usb_script = NULL;
|
||||||
|
|
||||||
if(app->interface == BadUsbHidInterfaceBle) {
|
scene_manager_set_scene_state(app->scene_manager, BadUsbSceneConfig, 0);
|
||||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
|
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
|
||||||
} else {
|
|
||||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigLayout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event.event == InputKeyOk) {
|
} else if(event.event == InputKeyOk) {
|
||||||
@@ -37,7 +34,9 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
|
|||||||
app->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb :
|
app->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb :
|
||||||
BadUsbHidInterfaceBle);
|
BadUsbHidInterfaceBle);
|
||||||
bad_usb_script_close(app->bad_usb_script);
|
bad_usb_script_close(app->bad_usb_script);
|
||||||
app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);
|
app->bad_usb_script = bad_usb_script_open(
|
||||||
|
app->file_path, &app->interface, &app->script_hid_cfg, false);
|
||||||
|
bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
|
||||||
} else {
|
} else {
|
||||||
bad_usb_script_pause_resume(app->bad_usb_script);
|
bad_usb_script_pause_resume(app->bad_usb_script);
|
||||||
}
|
}
|
||||||
@@ -45,6 +44,7 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
|
|||||||
}
|
}
|
||||||
} else if(event.type == SceneManagerEventTypeTick) {
|
} else if(event.type == SceneManagerEventTypeTick) {
|
||||||
bad_usb_view_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
|
bad_usb_view_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
|
||||||
|
bad_usb_view_set_interface(app->bad_usb_view, app->interface);
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,18 @@ void bad_usb_scene_work_on_enter(void* context) {
|
|||||||
|
|
||||||
bad_usb_view_set_interface(app->bad_usb_view, app->interface);
|
bad_usb_view_set_interface(app->bad_usb_view, app->interface);
|
||||||
|
|
||||||
app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);
|
// Opening script the first time:
|
||||||
|
// - copy user settings as base config
|
||||||
|
// - load ID/BLE_ID/BT_ID config if present
|
||||||
|
// Then disable this until next script selected so user can customize options
|
||||||
|
bool first_script_load = scene_manager_get_scene_state(app->scene_manager, BadUsbSceneWork);
|
||||||
|
if(first_script_load) {
|
||||||
|
memcpy(&app->script_hid_cfg, &app->user_hid_cfg, sizeof(app->script_hid_cfg));
|
||||||
|
scene_manager_set_scene_state(app->scene_manager, BadUsbSceneWork, false);
|
||||||
|
}
|
||||||
|
// Interface and config are passed as pointers as ID/BLE_ID/BT_ID config can modify them
|
||||||
|
app->bad_usb_script = bad_usb_script_open(
|
||||||
|
app->file_path, &app->interface, &app->script_hid_cfg, first_script_load);
|
||||||
bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
|
bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
|
||||||
|
|
||||||
FuriString* file_name;
|
FuriString* file_name;
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ typedef struct {
|
|||||||
static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||||
BadUsbModel* model = _model;
|
BadUsbModel* model = _model;
|
||||||
|
|
||||||
FuriString* disp_str;
|
FuriString* disp_str = furi_string_alloc_set(model->file_name);
|
||||||
disp_str = furi_string_alloc_set(model->file_name);
|
|
||||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
|
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
|
||||||
@@ -35,6 +34,8 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
} else {
|
} else {
|
||||||
furi_string_printf(disp_str, "(%s)", model->layout);
|
furi_string_printf(disp_str, "(%s)", model->layout);
|
||||||
}
|
}
|
||||||
|
uint32_t e = model->state.elapsed;
|
||||||
|
furi_string_cat_printf(disp_str, " %02lu:%02lu.%ld", e / 60 / 1000, e / 1000, e % 1000);
|
||||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
||||||
canvas_draw_str(
|
canvas_draw_str(
|
||||||
canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
|
canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
|
||||||
@@ -52,13 +53,8 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
if((state == BadUsbStateIdle) || (state == BadUsbStateDone) ||
|
if((state == BadUsbStateIdle) || (state == BadUsbStateDone) ||
|
||||||
(state == BadUsbStateNotConnected)) {
|
(state == BadUsbStateNotConnected)) {
|
||||||
elements_button_center(canvas, "Run");
|
elements_button_center(canvas, "Run");
|
||||||
if(model->interface == BadUsbHidInterfaceBle) {
|
|
||||||
elements_button_right(canvas, "USB");
|
|
||||||
elements_button_left(canvas, "Config");
|
elements_button_left(canvas, "Config");
|
||||||
} else {
|
elements_button_right(canvas, model->interface == BadUsbHidInterfaceBle ? "USB" : "BLE");
|
||||||
elements_button_right(canvas, "BLE");
|
|
||||||
elements_button_left(canvas, "Layout");
|
|
||||||
}
|
|
||||||
} else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) {
|
} else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) {
|
||||||
elements_button_center(canvas, "Stop");
|
elements_button_center(canvas, "Stop");
|
||||||
if(!model->pause_wait) {
|
if(!model->pause_wait) {
|
||||||
@@ -90,77 +86,85 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
|
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
|
||||||
} else if(state == BadUsbStateScriptError) {
|
} else if(state == BadUsbStateScriptError) {
|
||||||
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
|
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);
|
furi_string_printf(disp_str, "line %zu", model->state.error_line);
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
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);
|
furi_string_set_str(disp_str, model->state.error);
|
||||||
elements_string_fit_width(canvas, disp_str, canvas_width(canvas));
|
elements_string_fit_width(canvas, disp_str, canvas_width(canvas));
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
furi_string_reset(disp_str);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
|
||||||
} else if(state == BadUsbStateIdle) {
|
} else if(state == BadUsbStateIdle) {
|
||||||
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
|
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
|
||||||
|
furi_string_printf(disp_str, "0/%zu", model->state.line_nb);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
canvas_set_font(canvas, FontBigNumbers);
|
||||||
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0");
|
canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "0");
|
||||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14);
|
||||||
} else if(state == BadUsbStateRunning) {
|
} else if(state == BadUsbStateRunning) {
|
||||||
if(model->anim_frame == 0) {
|
if(model->anim_frame == 0) {
|
||||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
||||||
} else {
|
} else {
|
||||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21);
|
canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21);
|
||||||
}
|
}
|
||||||
|
furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
canvas_set_font(canvas, FontBigNumbers);
|
||||||
furi_string_printf(
|
furi_string_printf(
|
||||||
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
furi_string_reset(disp_str);
|
canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14);
|
||||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
|
||||||
} else if(state == BadUsbStateDone) {
|
} else if(state == BadUsbStateDone) {
|
||||||
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
|
||||||
|
furi_string_printf(disp_str, "%zu/%zu", model->state.line_nb, model->state.line_nb);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
canvas_set_font(canvas, FontBigNumbers);
|
||||||
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100");
|
canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "100");
|
||||||
furi_string_reset(disp_str);
|
canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14);
|
||||||
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
|
|
||||||
} else if(state == BadUsbStateDelay) {
|
} else if(state == BadUsbStateDelay) {
|
||||||
if(model->anim_frame == 0) {
|
if(model->anim_frame == 0) {
|
||||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
||||||
} else {
|
} else {
|
||||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
|
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
|
||||||
}
|
}
|
||||||
|
uint32_t delay = model->state.delay_remain / 10;
|
||||||
|
if(delay) {
|
||||||
|
furi_string_printf(disp_str, "Delay %lus", delay);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 4, 61, AlignLeft, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
|
}
|
||||||
|
furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
canvas_set_font(canvas, FontBigNumbers);
|
||||||
furi_string_printf(
|
furi_string_printf(
|
||||||
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
furi_string_reset(disp_str);
|
canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14);
|
||||||
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 == BadUsbStatePaused) || (state == BadUsbStateWaitForBtn)) {
|
} else if((state == BadUsbStatePaused) || (state == BadUsbStateWaitForBtn)) {
|
||||||
if(model->anim_frame == 0) {
|
if(model->anim_frame == 0) {
|
||||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
|
||||||
} else {
|
} else {
|
||||||
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
|
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
|
||||||
}
|
}
|
||||||
|
if(state != BadUsbStateWaitForBtn) {
|
||||||
|
canvas_draw_str_aligned(canvas, 4, 61, AlignLeft, AlignBottom, "Paused");
|
||||||
|
}
|
||||||
|
furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
canvas_set_font(canvas, FontBigNumbers);
|
||||||
furi_string_printf(
|
furi_string_printf(
|
||||||
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
furi_string_reset(disp_str);
|
canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14);
|
||||||
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 {
|
} else {
|
||||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -380,7 +380,8 @@ bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta)
|
|||||||
#define CONNECTION_INTERVAL_MAX (0x24)
|
#define CONNECTION_INTERVAL_MAX (0x24)
|
||||||
|
|
||||||
static GapConfig template_config = {
|
static GapConfig template_config = {
|
||||||
.adv_service = {
|
.adv_service =
|
||||||
|
{
|
||||||
.UUID_Type = UUID_TYPE_16,
|
.UUID_Type = UUID_TYPE_16,
|
||||||
.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
|
.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
|
||||||
},
|
},
|
||||||
@@ -412,19 +413,17 @@ static void ble_profile_hid_get_config(GapConfig* config, FuriHalBleProfileParam
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set advertise name
|
// Set advertise name
|
||||||
memset(config->adv_name, 0, sizeof(config->adv_name));
|
|
||||||
FuriString* name = furi_string_alloc_set(furi_hal_version_get_ble_local_device_name_ptr());
|
|
||||||
|
|
||||||
const char* clicker_str = "Control";
|
const char* clicker_str = "Control";
|
||||||
if(hid_profile_params && hid_profile_params->device_name_prefix) {
|
if(hid_profile_params && hid_profile_params->device_name_prefix) {
|
||||||
clicker_str = hid_profile_params->device_name_prefix;
|
clicker_str = hid_profile_params->device_name_prefix;
|
||||||
}
|
}
|
||||||
furi_string_replace_str(name, "Flipper", clicker_str);
|
snprintf(
|
||||||
if(furi_string_size(name) >= sizeof(config->adv_name)) {
|
config->adv_name,
|
||||||
furi_string_left(name, sizeof(config->adv_name) - 1);
|
sizeof(config->adv_name),
|
||||||
}
|
"%c%s %s",
|
||||||
memcpy(config->adv_name, furi_string_get_cstr(name), furi_string_size(name));
|
furi_hal_version_get_ble_local_device_name_ptr()[0],
|
||||||
furi_string_free(name);
|
clicker_str,
|
||||||
|
furi_hal_version_get_name_ptr());
|
||||||
}
|
}
|
||||||
|
|
||||||
static const FuriHalBleProfileTemplate profile_callbacks = {
|
static const FuriHalBleProfileTemplate profile_callbacks = {
|
||||||
|
|||||||
@@ -12,9 +12,6 @@
|
|||||||
|
|
||||||
#define HID_INTERVAL 2
|
#define HID_INTERVAL 2
|
||||||
|
|
||||||
#define HID_VID_DEFAULT 0x046D
|
|
||||||
#define HID_PID_DEFAULT 0xC529
|
|
||||||
|
|
||||||
struct HidIntfDescriptor {
|
struct HidIntfDescriptor {
|
||||||
struct usb_interface_descriptor hid;
|
struct usb_interface_descriptor hid;
|
||||||
struct usb_hid_descriptor hid_desc;
|
struct usb_hid_descriptor hid_desc;
|
||||||
|
|||||||
@@ -9,6 +9,11 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define HID_MANUF_PRODUCT_NAME_LEN 32
|
||||||
|
|
||||||
|
#define HID_VID_DEFAULT 0x046D
|
||||||
|
#define HID_PID_DEFAULT 0xC529
|
||||||
|
|
||||||
/** Max number of simultaneously pressed keys (keyboard) */
|
/** Max number of simultaneously pressed keys (keyboard) */
|
||||||
#define HID_KB_MAX_KEYS 6
|
#define HID_KB_MAX_KEYS 6
|
||||||
/** Max number of simultaneously pressed keys (consumer control) */
|
/** Max number of simultaneously pressed keys (consumer control) */
|
||||||
@@ -166,10 +171,11 @@ static const uint16_t hid_asciimap[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
// Note: vid/pid should be uint16_t and are treated as such
|
||||||
uint32_t vid;
|
uint32_t vid;
|
||||||
uint32_t pid;
|
uint32_t pid;
|
||||||
char manuf[32];
|
char manuf[HID_MANUF_PRODUCT_NAME_LEN];
|
||||||
char product[32];
|
char product[HID_MANUF_PRODUCT_NAME_LEN];
|
||||||
} FuriHalUsbHidConfig;
|
} FuriHalUsbHidConfig;
|
||||||
|
|
||||||
typedef void (*HidStateCallback)(bool state, void* context);
|
typedef void (*HidStateCallback)(bool state, void* context);
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FURI_HAL_VERSION_NAME_LENGTH 8
|
#define FURI_HAL_VERSION_NAME_LENGTH (8)
|
||||||
#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1)
|
#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1)
|
||||||
#define FURI_HAL_BT_ADV_NAME_LENGTH (18 + 1) // 18 characters + null terminator
|
/** 31b BLE Adv - 3b flags - 2b name prefix - 4b service uuid - 3b tx power = 19, + 1b null terminator (not present in packet) */
|
||||||
#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH \
|
#define FURI_HAL_BT_ADV_NAME_LENGTH (20)
|
||||||
(1 + FURI_HAL_BT_ADV_NAME_LENGTH) // Used for custom BT name, BLE symbol + name
|
/** BLE symbol + name */
|
||||||
|
#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + FURI_HAL_BT_ADV_NAME_LENGTH)
|
||||||
|
|
||||||
/** OTP Versions enum */
|
/** OTP Versions enum */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
Reference in New Issue
Block a user