From 3745ae22418b1a6b3860ff1c2c75454479a336a8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 6 Apr 2025 04:56:08 +0300 Subject: [PATCH] merge ofw pr 4136 [ci skip] BadUSB: Full USB/BLE parameter customization, UI improvements, and more by Willy-JL --- applications/main/bad_usb/application.fam | 2 +- applications/main/bad_usb/bad_usb_app.c | 160 +++++-- applications/main/bad_usb/bad_usb_app_i.h | 13 + .../main/bad_usb/helpers/bad_usb_hid.c | 56 ++- .../main/bad_usb/helpers/bad_usb_hid.h | 11 +- .../main/bad_usb/helpers/ble_hid_profile.c | 429 ++++++++++++++++++ .../main/bad_usb/helpers/ble_hid_profile.h | 109 +++++ .../main/bad_usb/helpers/ble_hid_service.c | 325 +++++++++++++ .../main/bad_usb/helpers/ble_hid_service.h | 31 ++ .../main/bad_usb/helpers/ducky_script.c | 174 ++++--- .../main/bad_usb/helpers/ducky_script.h | 7 +- .../bad_usb/helpers/ducky_script_commands.c | 2 + .../main/bad_usb/helpers/ducky_script_i.h | 6 +- .../bad_usb/helpers/ducky_script_keycodes.c | 28 +- .../badusb/assets/layouts/de-DE-mac.kl | Bin 0 -> 256 bytes .../bad_usb/scenes/bad_usb_scene_config.c | 219 ++++++++- .../bad_usb/scenes/bad_usb_scene_config.h | 6 +- .../scenes/bad_usb_scene_config_ble_mac.c | 73 +++ .../scenes/bad_usb_scene_config_ble_name.c | 62 +++ .../scenes/bad_usb_scene_config_layout.c | 10 +- .../scenes/bad_usb_scene_config_usb_name.c | 86 ++++ .../scenes/bad_usb_scene_config_usb_vidpid.c | 59 +++ .../scenes/bad_usb_scene_confirm_unpair.c | 3 +- ...ene_unpair_done.c => bad_usb_scene_done.c} | 12 +- .../scenes/bad_usb_scene_file_select.c | 4 +- .../main/bad_usb/scenes/bad_usb_scene_work.c | 25 +- .../main/bad_usb/views/bad_usb_view.c | 78 ++-- lib/ble_profile/extra_profiles/hid_profile.c | 33 +- targets/f7/furi_hal/furi_hal_usb_hid.c | 3 - targets/furi_hal_include/furi_hal_usb_hid.h | 10 +- targets/furi_hal_include/furi_hal_version.h | 11 +- 31 files changed, 1841 insertions(+), 206 deletions(-) create mode 100644 applications/main/bad_usb/helpers/ble_hid_profile.c create mode 100644 applications/main/bad_usb/helpers/ble_hid_profile.h create mode 100644 applications/main/bad_usb/helpers/ble_hid_service.c create mode 100644 applications/main/bad_usb/helpers/ble_hid_service.h create mode 100755 applications/main/bad_usb/resources/badusb/assets/layouts/de-DE-mac.kl create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_ble_mac.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_ble_name.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_usb_name.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_usb_vidpid.c rename applications/main/bad_usb/scenes/{bad_usb_scene_unpair_done.c => bad_usb_scene_done.c} (67%) diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_usb/application.fam index 8d3909fcc..9844e248d 100644 --- a/applications/main/bad_usb/application.fam +++ b/applications/main/bad_usb/application.fam @@ -7,7 +7,7 @@ App( icon="A_BadUsb_14", order=70, resources="resources", - fap_libs=["assets", "ble_profile"], + fap_libs=["assets"], fap_icon="icon.png", fap_category="USB", ) diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index eda702cf4..96ccb14ed 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -31,52 +31,123 @@ static void bad_usb_app_tick_event_callback(void* context) { static void bad_usb_load_settings(BadUsbApp* app) { Storage* storage = furi_record_open(RECORD_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(); - uint32_t version = 0; - uint32_t interface = 0; + uint32_t temp_uint = 0; if(flipper_format_file_open_existing(fff, BAD_USB_SETTINGS_PATH)) { 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) || - (version != BAD_USB_SETTINGS_VERSION)) + (temp_uint != BAD_USB_SETTINGS_VERSION)) break; - if(!flipper_format_read_string(fff, "layout", temp_str)) break; - if(!flipper_format_read_uint32(fff, "interface", &interface, 1)) break; - if(interface > BadUsbHidInterfaceBle) break; + if(flipper_format_read_string(fff, "layout", temp_str)) { + furi_string_set(app->keyboard_layout, temp_str); + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat( + storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); + if((file_check_err != FSE_OK) || (layout_file_info.size != 256)) { + furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT); + } + } else { + furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT); + flipper_format_rewind(fff); + } - state = true; + 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); } - flipper_format_free(fff); - furi_record_close(RECORD_STORAGE); - - if(state) { - furi_string_set(app->keyboard_layout, temp_str); - app->interface = interface; - - Storage* fs_api = furi_record_open(RECORD_STORAGE); - FileInfo layout_file_info; - FS_Error file_check_err = storage_common_stat( - fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); - furi_record_close(RECORD_STORAGE); - if((file_check_err != FSE_OK) || (layout_file_info.size != 256)) { - furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT); - } - } else { - furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT); - app->interface = BadUsbHidInterfaceUsb; - } 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) { Storage* storage = furi_record_open(RECORD_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)) { do { @@ -84,9 +155,19 @@ static void bad_usb_save_settings(BadUsbApp* app) { fff, BAD_USB_SETTINGS_FILE_TYPE, BAD_USB_SETTINGS_VERSION)) break; if(!flipper_format_write_string(fff, "layout", app->keyboard_layout)) break; - uint32_t interface_id = app->interface; - if(!flipper_format_write_uint32(fff, "interface", (const uint32_t*)&interface_id, 1)) + temp_uint = app->interface; + 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; + 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); } @@ -121,7 +202,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_set_event_callback_context(app->view_dispatcher, app); view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_usb_app_tick_event_callback, 500); + app->view_dispatcher, bad_usb_app_tick_event_callback, 250); view_dispatcher_set_custom_event_callback( app->view_dispatcher, bad_usb_app_custom_event_callback); view_dispatcher_set_navigation_event_callback( @@ -146,6 +227,14 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_add_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); 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)); 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); } else { 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); 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_free(app->view_dispatcher); scene_manager_free(app->scene_manager); diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index b34bd5de6..06a798706 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include "views/bad_usb_view.h" @@ -36,6 +38,13 @@ struct BadUsbApp { Widget* widget; Popup* popup; 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; FuriString* file_path; @@ -44,6 +53,8 @@ struct BadUsbApp { BadUsbScript* bad_usb_script; BadUsbHidInterface interface; + BadUsbHidConfig user_hid_cfg; + BadUsbHidConfig script_hid_cfg; FuriHalUsbInterface* usb_if_prev; }; @@ -52,6 +63,8 @@ typedef enum { BadUsbAppViewPopup, BadUsbAppViewWork, BadUsbAppViewConfig, + BadUsbAppViewByteInput, + BadUsbAppViewTextInput, } BadUsbAppView; void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface); diff --git a/applications/main/bad_usb/helpers/bad_usb_hid.c b/applications/main/bad_usb/helpers/bad_usb_hid.c index c6226cf37..5ae4146e8 100644 --- a/applications/main/bad_usb/helpers/bad_usb_hid.c +++ b/applications/main/bad_usb/helpers/bad_usb_hid.c @@ -1,5 +1,5 @@ #include "bad_usb_hid.h" -#include +#include "ble_hid_profile.h" #include #include @@ -7,8 +7,14 @@ #define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" -void* hid_usb_init(FuriHalUsbHidConfig* hid_cfg) { - furi_check(furi_hal_usb_set_config(&usb_hid, hid_cfg)); +void hid_usb_adjust_config(BadUsbHidConfig* 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; } @@ -86,6 +92,7 @@ uint8_t hid_usb_get_led_state(void* inst) { } static const BadUsbHidApi hid_api_usb = { + .adjust_config = hid_usb_adjust_config, .init = hid_usb_init, .deinit = hid_usb_deinit, .set_state_callback = hid_usb_set_state_callback, @@ -111,11 +118,6 @@ typedef struct { bool is_connected; } BleHidInstance; -static const BleProfileHidParams ble_hid_params = { - .device_name_prefix = "BadUSB", - .mac_xor = 0x0002, -}; - static void hid_ble_connection_status_callback(BtStatus status, void* context) { furi_assert(context); BleHidInstance* ble_hid = context; @@ -125,8 +127,38 @@ static void hid_ble_connection_status_callback(BtStatus status, void* context) { } } -void* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) { - UNUSED(hid_cfg); +void hid_ble_adjust_config(BadUsbHidConfig* 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)); ble_hid->bt = furi_record_open(RECORD_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)); - 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_hal_bt_start_advertising(); @@ -236,6 +269,7 @@ uint8_t hid_ble_get_led_state(void* inst) { } static const BadUsbHidApi hid_api_ble = { + .adjust_config = hid_ble_adjust_config, .init = hid_ble_init, .deinit = hid_ble_deinit, .set_state_callback = hid_ble_set_state_callback, diff --git a/applications/main/bad_usb/helpers/bad_usb_hid.h b/applications/main/bad_usb/helpers/bad_usb_hid.h index e4758ab68..8749bdc3b 100644 --- a/applications/main/bad_usb/helpers/bad_usb_hid.h +++ b/applications/main/bad_usb/helpers/bad_usb_hid.h @@ -7,13 +7,22 @@ extern "C" { #include #include +#include "ble_hid_profile.h" + typedef enum { BadUsbHidInterfaceUsb, BadUsbHidInterfaceBle, + BadUsbHidInterfaceMAX, } BadUsbHidInterface; 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 (*set_state_callback)(void* inst, HidStateCallback cb, void* context); bool (*is_connected)(void* inst); diff --git a/applications/main/bad_usb/helpers/ble_hid_profile.c b/applications/main/bad_usb/helpers/ble_hid_profile.c new file mode 100644 index 000000000..a4f32159e --- /dev/null +++ b/applications/main/bad_usb/helpers/ble_hid_profile.c @@ -0,0 +1,429 @@ +#include "ble_hid_profile.h" + +// Based on + +#include +#include +#include +#include "ble_hid_service.h" + +#include +#include +#include + +#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; diff --git a/applications/main/bad_usb/helpers/ble_hid_profile.h b/applications/main/bad_usb/helpers/ble_hid_profile.h new file mode 100644 index 000000000..2302aa581 --- /dev/null +++ b/applications/main/bad_usb/helpers/ble_hid_profile.h @@ -0,0 +1,109 @@ +#pragma once + +// Based on + +#include + +#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 diff --git a/applications/main/bad_usb/helpers/ble_hid_service.c b/applications/main/bad_usb/helpers/ble_hid_service.c new file mode 100644 index 000000000..b546368dd --- /dev/null +++ b/applications/main/bad_usb/helpers/ble_hid_service.c @@ -0,0 +1,325 @@ +#include "ble_hid_service.h" + +// Based on + +#include "app_common.h" // IWYU pragma: keep +#include +#include +#include + +#include +#include + +#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); +} diff --git a/applications/main/bad_usb/helpers/ble_hid_service.h b/applications/main/bad_usb/helpers/ble_hid_service.h new file mode 100644 index 000000000..e1ac3b0be --- /dev/null +++ b/applications/main/bad_usb/helpers/ble_hid_service.h @@ -0,0 +1,31 @@ +#pragma once + +// Based on + +#include +#include + +#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 diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index 379c3e24a..a64629af2 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -25,6 +25,8 @@ typedef enum { } WorkerEvtFlags; 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] = { 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 len = strlen(line); - for(uint32_t i = 0; i < len; i++) { - if(line[i] == ' ') return i; - } - return 0; + char* first_space = strchr(line, ' '); + return first_space ? (first_space - line) : 0; } 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) { 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) { 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 - 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) { return cmd_result; } // 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) { bad_usb->hid->mouse_press(bad_usb->hid_inst, key); bad_usb->hid->mouse_release(bad_usb->hid_inst, key); return 0; } - // Special keys + modifiers - key = ducky_get_keycode(bad_usb, line_tmp, false); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_usb, "No keycode defined for %s", line_tmp); - } - if((key & 0xFF00) != 0) { - // It's a modifier key - uint32_t offset = ducky_get_command_len(line_tmp) + 1; - // ducky_get_command_len() returns 0 without space, so check for != 1 - if(offset != 1 && line_len > offset) { - // It's also a key combination - key |= ducky_get_keycode(bad_usb, line_tmp + offset, true); - } + // Parse chain of modifiers linked by spaces and hyphens + uint16_t modifiers = 0; + while(1) { + key = ducky_get_next_modifier_keycode_by_name(&line_cstr); + if(key == HID_KEYBOARD_NONE) break; + + modifiers |= key; + char next_char = *line_cstr; + if(next_char == ' ' || next_char == '-') line_cstr++; } + + // 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_release(bad_usb->hid_inst, key); return 0; } 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) { - bad_usb->hid_cfg.manuf[0] = '\0'; - bad_usb->hid_cfg.product[0] = '\0'; + FuriHalUsbHidConfig* usb_hid_cfg = &bad_usb->hid_cfg->usb; + + 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); if(!ducky_is_line_end(line[id_len + 1])) { sscanf( &line[id_len + 1], "%31[^\r\n:]:%31[^\r\n]", - bad_usb->hid_cfg.manuf, - bad_usb->hid_cfg.product); + usb_hid_cfg->manuf, + usb_hid_cfg->product); } FURI_LOG_D( WORKER_TAG, "set id: %04lX:%04lX mfr:%s product:%s", - bad_usb->hid_cfg.vid, - bad_usb->hid_cfg.pid, - bad_usb->hid_cfg.manuf, - bad_usb->hid_cfg.product); + usb_hid_cfg->vid, + usb_hid_cfg->pid, + usb_hid_cfg->manuf, + usb_hid_cfg->product); return true; } 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) { furi_assert(context); BadUsbScript* bad_usb = context; @@ -283,17 +311,30 @@ static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { } } while(ret > 0); - const char* line_tmp = furi_string_get_cstr(bad_usb->line); - bool id_set = false; // Looking for ID command at first line - if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { - id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]); + if(bad_usb->load_id_cfg) { + const char* line_tmp = furi_string_get_cstr(bad_usb->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(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; + } + } + + // Auto-switch based on ID/BLE_ID/BT_ID command, user can override manually after + if(interface != *bad_usb->interface) { + *bad_usb->interface = interface; + bad_usb->hid = bad_usb_hid_get_interface(*bad_usb->interface); + } } - if(id_set) { - bad_usb->hid_inst = bad_usb->hid->init(&bad_usb->hid_cfg); - } else { - bad_usb->hid_inst = bad_usb->hid->init(NULL); - } + 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); 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_prev = furi_string_alloc(); bad_usb->string_print = furi_string_alloc(); + bad_usb->st.elapsed = 0; while(1) { + uint32_t start = furi_get_tick(); if(worker_state == BadUsbStateInit) { // State: initialization + start = 0; if(storage_file_open( script_file, 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)) { worker_state = BadUsbStateIdle; // Ready to run } else { - worker_state = BadUsbStateNotConnected; // USB not connected + worker_state = BadUsbStateNotConnected; // Not connected } } else { worker_state = BadUsbStateScriptError; // Script preload error @@ -419,7 +463,8 @@ static int32_t bad_usb_worker(void* context) { } 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( WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, FuriWaitForever); @@ -429,11 +474,12 @@ static int32_t bad_usb_worker(void* context) { } else if(flags & WorkerEvtConnect) { worker_state = BadUsbStateIdle; // Ready to run } 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; } else if(worker_state == BadUsbStateIdle) { // State: ready to start + start = 0; uint32_t flags = bad_usb_flags_get( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever); @@ -452,12 +498,14 @@ static int32_t bad_usb_worker(void* context) { bad_usb->file_end = false; storage_file_seek(script_file, 0, true); worker_state = BadUsbStateRunning; + bad_usb->st.elapsed = 0; } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected + worker_state = BadUsbStateNotConnected; // Disconnected } bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateWillRun) { // State: start on connection + start = 0; uint32_t flags = bad_usb_flags_get( WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); @@ -482,6 +530,7 @@ static int32_t bad_usb_worker(void* context) { if(flags == (unsigned)FuriFlagErrorTimeout) { // If nothing happened - start script execution worker_state = BadUsbStateRunning; + bad_usb->st.elapsed = 0; } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateIdle; furi_thread_flags_clear(WorkerEvtStartStop); @@ -492,7 +541,7 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; } 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( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, FuriFlagWaitAny, @@ -506,19 +555,21 @@ static int32_t bad_usb_worker(void* context) { worker_state = BadUsbStateIdle; // Stop executing script bad_usb->hid->release_all(bad_usb->hid_inst); } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected + worker_state = BadUsbStateNotConnected; // Disconnected bad_usb->hid->release_all(bad_usb->hid_inst); } else if(flags & WorkerEvtPauseResume) { pause_state = BadUsbStateRunning; worker_state = BadUsbStatePaused; // Pause } bad_usb->st.state = worker_state; + bad_usb->st.elapsed += (furi_get_tick() - start); continue; } else if( (flags == (unsigned)FuriFlagErrorTimeout) || (flags == (unsigned)FuriFlagErrorResource)) { if(delay_val > 0) { bad_usb->st.delay_remain--; + bad_usb->st.elapsed += (furi_get_tick() - start); continue; } bad_usb->st.state = BadUsbStateRunning; @@ -533,6 +584,7 @@ static int32_t bad_usb_worker(void* context) { worker_state = BadUsbStateIdle; bad_usb->st.state = BadUsbStateDone; bad_usb->hid->release_all(bad_usb->hid_inst); + bad_usb->st.elapsed += (furi_get_tick() - start); continue; } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays 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 worker_state = BadUsbStateWaitForBtn; 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.delay_remain = delay_val / 1000; + bad_usb->st.delay_remain = delay_val / 100; } } else { furi_check((flags & FuriFlagError) == 0); } } else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press + start = 0; uint32_t flags = bad_usb_flags_get( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, FuriWaitForever); @@ -559,13 +612,14 @@ static int32_t bad_usb_worker(void* context) { delay_val = 0; worker_state = BadUsbStateRunning; } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected + worker_state = BadUsbStateNotConnected; // Disconnected bad_usb->hid->release_all(bad_usb->hid_inst); } bad_usb->st.state = worker_state; continue; } } else if(worker_state == BadUsbStatePaused) { // State: Paused + start = 0; uint32_t flags = bad_usb_flags_get( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, FuriWaitForever); @@ -577,14 +631,14 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; bad_usb->hid->release_all(bad_usb->hid_inst); } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected + worker_state = BadUsbStateNotConnected; // Disconnected bad_usb->st.state = worker_state; bad_usb->hid->release_all(bad_usb->hid_inst); } else if(flags & WorkerEvtPauseResume) { if(pause_state == BadUsbStateRunning) { if(delay_val > 0) { bad_usb->st.state = BadUsbStateDelay; - bad_usb->st.delay_remain = delay_val / 1000; + bad_usb->st.delay_remain = delay_val / 100; } else { bad_usb->st.state = BadUsbStateRunning; delay_val = 0; @@ -611,13 +665,14 @@ static int32_t bad_usb_worker(void* context) { worker_state = BadUsbStateIdle; // Stop executing script bad_usb->hid->release_all(bad_usb->hid_inst); } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected + worker_state = BadUsbStateNotConnected; // Disconnected bad_usb->hid->release_all(bad_usb->hid_inst); } else if(flags & WorkerEvtPauseResume) { pause_state = BadUsbStateStringDelay; worker_state = BadUsbStatePaused; // Pause } bad_usb->st.state = worker_state; + bad_usb->st.elapsed += (furi_get_tick() - start); continue; } else if( (flags == (unsigned)FuriFlagErrorTimeout) || @@ -633,6 +688,7 @@ static int32_t bad_usb_worker(void* context) { } else if( (worker_state == BadUsbStateFileError) || (worker_state == BadUsbStateScriptError)) { // State: error + start = 0; uint32_t flags = bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command @@ -640,6 +696,9 @@ static int32_t bad_usb_worker(void* context) { break; } } + if(start) { + bad_usb->st.elapsed += (furi_get_tick() - start); + } } 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))); } -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); 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.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); furi_thread_start(bad_usb->thread); diff --git a/applications/main/bad_usb/helpers/ducky_script.h b/applications/main/bad_usb/helpers/ducky_script.h index 9519623f6..9131ef43e 100644 --- a/applications/main/bad_usb/helpers/ducky_script.h +++ b/applications/main/bad_usb/helpers/ducky_script.h @@ -30,11 +30,16 @@ typedef struct { uint32_t delay_remain; size_t error_line; char error[64]; + uint32_t elapsed; } BadUsbState; 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); diff --git a/applications/main/bad_usb/helpers/ducky_script_commands.c b/applications/main/bad_usb/helpers/ducky_script_commands.c index 1b4ff55cb..6c6fe36c7 100644 --- a/applications/main/bad_usb/helpers/ducky_script_commands.c +++ b/applications/main/bad_usb/helpers/ducky_script_commands.c @@ -256,6 +256,8 @@ static int32_t ducky_fnc_mouse_move(BadUsbScript* bad_usb, const char* line, int static const DuckyCmd ducky_commands[] = { {"REM", NULL, -1}, {"ID", NULL, -1}, + {"BT_ID", NULL, -1}, + {"BLE_ID", NULL, -1}, {"DELAY", ducky_fnc_delay, -1}, {"STRING", ducky_fnc_string, 0}, {"STRINGLN", ducky_fnc_string, 1}, diff --git a/applications/main/bad_usb/helpers/ducky_script_i.h b/applications/main/bad_usb/helpers/ducky_script_i.h index fd95ecf58..d735a8407 100644 --- a/applications/main/bad_usb/helpers/ducky_script_i.h +++ b/applications/main/bad_usb/helpers/ducky_script_i.h @@ -22,7 +22,9 @@ extern "C" { #define HID_MOUSE_NONE 0 struct BadUsbScript { - FuriHalUsbHidConfig hid_cfg; + BadUsbHidInterface* interface; + BadUsbHidConfig* hid_cfg; + bool load_id_cfg; const BadUsbHidApi* hid; void* hid_inst; FuriThread* thread; @@ -54,6 +56,8 @@ uint32_t ducky_get_command_len(const char* line); 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_media_keycode_by_name(const char* param); diff --git a/applications/main/bad_usb/helpers/ducky_script_keycodes.c b/applications/main/bad_usb/helpers/ducky_script_keycodes.c index 7dd2e4d16..ce957bb4e 100644 --- a/applications/main/bad_usb/helpers/ducky_script_keycodes.c +++ b/applications/main/bad_usb/helpers/ducky_script_keycodes.c @@ -6,21 +6,16 @@ typedef struct { uint16_t keycode; } DuckyKey; -static const DuckyKey ducky_keys[] = { - {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, - {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, - {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, - {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, - {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, - {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL}, - +static const DuckyKey ducky_modifier_keys[] = { {"CTRL", KEY_MOD_LEFT_CTRL}, {"CONTROL", KEY_MOD_LEFT_CTRL}, {"SHIFT", KEY_MOD_LEFT_SHIFT}, {"ALT", KEY_MOD_LEFT_ALT}, {"GUI", KEY_MOD_LEFT_GUI}, {"WINDOWS", KEY_MOD_LEFT_GUI}, +}; +static const DuckyKey ducky_keys[] = { {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, {"DOWN", HID_KEYBOARD_DOWN_ARROW}, {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, @@ -119,6 +114,23 @@ static const DuckyKey ducky_mouse_keys[] = { {"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) { for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) { size_t key_cmd_len = strlen(ducky_keys[i].name); diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/de-DE-mac.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/de-DE-mac.kl new file mode 100755 index 0000000000000000000000000000000000000000..471b7143e24c8351d253885436bd60fb7cc4a641 GIT binary patch literal 256 zcmaLL#|^?z0Kibog3t+}_mT#p2@aG2{wVA}17|My_6)xrIdS-1e|2W<#ydMxGw&9b z-n{YT&5PVUYc_1zk&=;9Q1bHWgS`(g#-U=>$eMKinterface == 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) { 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) { VariableItemList* var_item_list = bad_usb->var_item_list; + VariableItem* item; variable_item_list_reset(var_item_list); variable_item_list_add(var_item_list, "Keyboard Layout (global)", 0, NULL, NULL); - variable_item_list_add(var_item_list, "Remove Pairing", 0, NULL, NULL); + 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) { @@ -28,7 +133,8 @@ void bad_usb_scene_config_on_enter(void* context) { variable_item_list_set_enter_callback( var_item_list, bad_usb_scene_config_select_callback, bad_usb); draw_menu(bad_usb); - variable_item_list_set_selected_item(var_item_list, 0); + 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); } @@ -38,13 +144,110 @@ bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); 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); - } else if(event.event == ConfigIndexBleUnpair) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfirmUnpair); + 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); + break; + default: + break; + } } 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; + } } } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h index 3d1b8b1a7..2ea25e134 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.h +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.h @@ -3,5 +3,9 @@ ADD_SCENE(bad_usb, work, Work) ADD_SCENE(bad_usb, error, Error) ADD_SCENE(bad_usb, config, Config) ADD_SCENE(bad_usb, config_layout, ConfigLayout) +ADD_SCENE(bad_usb, 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, unpair_done, UnpairDone) +ADD_SCENE(bad_usb, done, Done) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_mac.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_mac.c new file mode 100644 index 000000000..7ad4e3ed4 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_mac.c @@ -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, ""); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_name.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_name.c new file mode 100644 index 000000000..af7913e7d --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_name.c @@ -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); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c index 3f01d7090..80ab44ab3 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c @@ -29,21 +29,17 @@ static bool bad_usb_layout_select(BadUsbApp* bad_usb) { void bad_usb_scene_config_layout_on_enter(void* context) { BadUsbApp* bad_usb = context; - if(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); - } + bad_usb_layout_select(bad_usb); + + scene_manager_previous_scene(bad_usb->scene_manager); } bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); - // BadUsbApp* bad_usb = context; return false; } void bad_usb_scene_config_layout_on_exit(void* context) { UNUSED(context); - // BadUsbApp* bad_usb = context; } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_name.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_name.c new file mode 100644 index 000000000..d0e136634 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_name.c @@ -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); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_vidpid.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_vidpid.c new file mode 100644 index 000000000..ce0c51a47 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_vidpid.c @@ -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, ""); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_confirm_unpair.c b/applications/main/bad_usb/scenes/bad_usb_scene_confirm_unpair.c index b8fd993e2..cd600386c 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_confirm_unpair.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_confirm_unpair.c @@ -36,7 +36,8 @@ bool bad_usb_scene_confirm_unpair_on_event(void* context, SceneManagerEvent even if(event.type == SceneManagerEventTypeCustom) { consumed = true; 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) { scene_manager_previous_scene(scene_manager); } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c b/applications/main/bad_usb/scenes/bad_usb_scene_done.c similarity index 67% rename from applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c rename to applications/main/bad_usb/scenes/bad_usb_scene_done.c index 9583f9bfb..9a878d889 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_done.c @@ -1,19 +1,17 @@ #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; 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; Popup* popup = bad_usb->popup; - bad_usb_hid_ble_remove_pairing(); - popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58); popup_set_header(popup, "Done", 20, 19, AlignLeft, AlignBottom); - popup_set_callback(popup, bad_usb_scene_unpair_done_popup_callback); + popup_set_callback(popup, bad_usb_scene_done_popup_callback); popup_set_context(popup, bad_usb); popup_set_timeout(popup, 1500); 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); } -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; UNUSED(bad_usb); UNUSED(event); @@ -30,7 +28,7 @@ bool bad_usb_scene_unpair_done_on_event(void* context, SceneManagerEvent event) 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; Popup* popup = bad_usb->popup; UNUSED(popup); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index 5e2c3f14b..9aa6f3eea 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -1,5 +1,4 @@ #include "../bad_usb_app_i.h" -#include #include 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)) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneWork, true); scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); } else { 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) { UNUSED(context); UNUSED(event); - // BadUsbApp* bad_usb = context; return false; } void bad_usb_scene_file_select_on_exit(void* context) { UNUSED(context); - // BadUsbApp* bad_usb = context; } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 0382b0f8e..d57f7eb33 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -20,11 +20,8 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { bad_usb_script_close(app->bad_usb_script); app->bad_usb_script = NULL; - if(app->interface == BadUsbHidInterfaceBle) { - scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); - } else { - scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigLayout); - } + scene_manager_set_scene_state(app->scene_manager, BadUsbSceneConfig, 0); + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); } consumed = true; } else if(event.event == InputKeyOk) { @@ -37,7 +34,9 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { app->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb : BadUsbHidInterfaceBle); bad_usb_script_close(app->bad_usb_script); - app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface); + 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 { 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) { 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; } @@ -54,7 +54,18 @@ void bad_usb_scene_work_on_enter(void* context) { 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); FuriString* file_name; diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 1a6f77958..4032ea974 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -24,8 +24,7 @@ typedef struct { static void bad_usb_draw_callback(Canvas* canvas, void* _model) { BadUsbModel* model = _model; - FuriString* disp_str; - disp_str = furi_string_alloc_set(model->file_name); + FuriString* disp_str = furi_string_alloc_set(model->file_name); elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); @@ -35,6 +34,8 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { } else { 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); canvas_draw_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) || (state == BadUsbStateNotConnected)) { elements_button_center(canvas, "Run"); - if(model->interface == BadUsbHidInterfaceBle) { - elements_button_right(canvas, "USB"); - elements_button_left(canvas, "Config"); - } else { - elements_button_right(canvas, "BLE"); - elements_button_left(canvas, "Layout"); - } + elements_button_left(canvas, "Config"); + elements_button_right(canvas, model->interface == BadUsbHidInterfaceBle ? "USB" : "BLE"); } else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) { elements_button_center(canvas, "Stop"); 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"); } else if(state == BadUsbStateScriptError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); - canvas_set_font(canvas, FontSecondary); furi_string_printf(disp_str, "line %zu", model->state.error_line); canvas_draw_str_aligned( canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - furi_string_set_str(disp_str, model->state.error); elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); canvas_draw_str_aligned( canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); } else if(state == BadUsbStateIdle) { 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_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadUsbStateRunning) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { 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); furi_string_printf( disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadUsbStateDone) { 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_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "100"); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadUsbStateDelay) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { 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); furi_string_printf( disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); - canvas_draw_str_aligned( - canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if((state == BadUsbStatePaused) || (state == BadUsbStateWaitForBtn)) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { 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); furi_string_printf( disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, "Paused"); - furi_string_reset(disp_str); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); } diff --git a/lib/ble_profile/extra_profiles/hid_profile.c b/lib/ble_profile/extra_profiles/hid_profile.c index ea90d3114..ed67f44a1 100644 --- a/lib/ble_profile/extra_profiles/hid_profile.c +++ b/lib/ble_profile/extra_profiles/hid_profile.c @@ -9,12 +9,12 @@ #include #include -#define HID_INFO_BASE_USB_SPECIFICATION (0x0101) -#define HID_INFO_COUNTRY_CODE (0x00) -#define BLE_PROFILE_HID_INFO_FLAG_REMOTE_WAKE_MSK (0x01) +#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_HID_KB_MAX_KEYS (6) #define BLE_PROFILE_CONSUMER_MAX_KEYS (1) // Report ids cant be 0 @@ -380,10 +380,11 @@ bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta) #define CONNECTION_INTERVAL_MAX (0x24) static GapConfig template_config = { - .adv_service = { - .UUID_Type = UUID_TYPE_16, - .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, - }, + .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, @@ -412,19 +413,17 @@ static void ble_profile_hid_get_config(GapConfig* config, FuriHalBleProfileParam } // 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"; if(hid_profile_params && hid_profile_params->device_name_prefix) { clicker_str = hid_profile_params->device_name_prefix; } - furi_string_replace_str(name, "Flipper", clicker_str); - if(furi_string_size(name) >= sizeof(config->adv_name)) { - furi_string_left(name, sizeof(config->adv_name) - 1); - } - memcpy(config->adv_name, furi_string_get_cstr(name), furi_string_size(name)); - furi_string_free(name); + snprintf( + config->adv_name, + sizeof(config->adv_name), + "%c%s %s", + furi_hal_version_get_ble_local_device_name_ptr()[0], + clicker_str, + furi_hal_version_get_name_ptr()); } static const FuriHalBleProfileTemplate profile_callbacks = { diff --git a/targets/f7/furi_hal/furi_hal_usb_hid.c b/targets/f7/furi_hal/furi_hal_usb_hid.c index c83261226..04f2ae400 100644 --- a/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -12,9 +12,6 @@ #define HID_INTERVAL 2 -#define HID_VID_DEFAULT 0x046D -#define HID_PID_DEFAULT 0xC529 - struct HidIntfDescriptor { struct usb_interface_descriptor hid; struct usb_hid_descriptor hid_desc; diff --git a/targets/furi_hal_include/furi_hal_usb_hid.h b/targets/furi_hal_include/furi_hal_usb_hid.h index af4a542de..19c9ed659 100644 --- a/targets/furi_hal_include/furi_hal_usb_hid.h +++ b/targets/furi_hal_include/furi_hal_usb_hid.h @@ -9,6 +9,11 @@ extern "C" { #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) */ #define HID_KB_MAX_KEYS 6 /** Max number of simultaneously pressed keys (consumer control) */ @@ -166,10 +171,11 @@ static const uint16_t hid_asciimap[] = { }; typedef struct { + // Note: vid/pid should be uint16_t and are treated as such uint32_t vid; uint32_t pid; - char manuf[32]; - char product[32]; + char manuf[HID_MANUF_PRODUCT_NAME_LEN]; + char product[HID_MANUF_PRODUCT_NAME_LEN]; } FuriHalUsbHidConfig; typedef void (*HidStateCallback)(bool state, void* context); diff --git a/targets/furi_hal_include/furi_hal_version.h b/targets/furi_hal_include/furi_hal_version.h index 2c098d482..1d026539a 100644 --- a/targets/furi_hal_include/furi_hal_version.h +++ b/targets/furi_hal_include/furi_hal_version.h @@ -14,11 +14,12 @@ extern "C" { #endif -#define FURI_HAL_VERSION_NAME_LENGTH 8 -#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 -#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH \ - (1 + FURI_HAL_BT_ADV_NAME_LENGTH) // Used for custom BT name, BLE symbol + name +#define FURI_HAL_VERSION_NAME_LENGTH (8) +#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1) +/** 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_BT_ADV_NAME_LENGTH (20) +/** BLE symbol + name */ +#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + FURI_HAL_BT_ADV_NAME_LENGTH) /** OTP Versions enum */ typedef enum {