Adds BLE adv name & mac changing effective

This commit is contained in:
yocvito
2023-01-25 19:00:41 +01:00
44 changed files with 2070 additions and 59 deletions

View File

@@ -0,0 +1,18 @@
App(
appid="bad_ble",
name="Bad BLE",
apptype=FlipperAppType.EXTERNAL,
entry_point="bad_ble_app",
cdefines=["APP_BAD_BLE"],
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
# icon="A_BadUsb_14",
order=70,
fap_category="Main",
fap_icon="badusb_10px.png",
fap_icon_assets="images",
fap_libs=["assets"],
)

View File

@@ -0,0 +1,195 @@
#include "bad_ble_app_i.h"
#include "bad_ble_settings_filename.h"
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <bt/bt_service/bt_i.h>
#include <bt/bt_service/bt.h>
#define BAD_BLE_SETTINGS_PATH BAD_BLE_APP_BASE_FOLDER "/" BAD_BLE_SETTINGS_FILE_NAME
static bool bad_ble_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
BadBleApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool bad_ble_app_back_event_callback(void* context) {
furi_assert(context);
BadBleApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void bad_ble_app_tick_event_callback(void* context) {
furi_assert(context);
BadBleApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
static void bad_ble_load_settings(BadBleApp* app) {
File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
if(storage_file_open(settings_file, BAD_BLE_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
char chr;
while((storage_file_read(settings_file, &chr, 1) == 1) &&
!storage_file_eof(settings_file) && !isspace(chr)) {
furi_string_push_back(app->keyboard_layout, chr);
}
}
storage_file_close(settings_file);
storage_file_free(settings_file);
}
static void bad_ble_save_settings(BadBleApp* app) {
File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
if(storage_file_open(settings_file, BAD_BLE_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
storage_file_write(
settings_file,
furi_string_get_cstr(app->keyboard_layout),
furi_string_size(app->keyboard_layout));
storage_file_write(settings_file, "\n", 1);
}
storage_file_close(settings_file);
storage_file_free(settings_file);
}
void bad_ble_set_name(BadBleApp* app, const char* fmt, ...) {
furi_assert(app);
va_list args;
va_start(args, fmt);
vsnprintf(app->name, BAD_BLE_ADV_NAME_MAX_LEN, fmt, args);
va_end(args);
}
BadBleApp* bad_ble_app_alloc(char* arg) {
BadBleApp* app = malloc(sizeof(BadBleApp));
app->bad_ble_script = NULL;
app->file_path = furi_string_alloc();
app->keyboard_layout = furi_string_alloc();
if(arg && strlen(arg)) {
furi_string_set(app->file_path, arg);
}
bad_ble_load_settings(app);
app->gui = furi_record_open(RECORD_GUI);
app->notifications = furi_record_open(RECORD_NOTIFICATION);
app->dialogs = furi_record_open(RECORD_DIALOGS);
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
app->scene_manager = scene_manager_alloc(&bad_ble_scene_handlers, app);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, bad_ble_app_tick_event_callback, 500);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, bad_ble_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, bad_ble_app_back_event_callback);
Bt* bt = furi_record_open(RECORD_BT);
app->bt = bt;
const char *adv_name = bt_get_profile_adv_name(bt);
memcpy(app->name, adv_name, BAD_BLE_ADV_NAME_MAX_LEN);
const uint8_t *mac_addr = bt_get_profile_mac_address(bt);
memcpy(app->mac, mac_addr, BAD_BLE_MAC_ADDRESS_LEN);
// Custom Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBleAppViewError, widget_get_view(app->widget));
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBleAppViewConfig, submenu_get_view(app->submenu));
app->bad_ble_view = bad_ble_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBleAppViewWork, bad_ble_get_view(app->bad_ble_view));
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBleAppViewConfigName, text_input_get_view(app->text_input));
app->byte_input = byte_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBleAppViewConfigMac, byte_input_get_view(app->byte_input));
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
if(!furi_string_empty(app->file_path)) {
app->bad_ble_script = bad_ble_script_open(app->file_path, bt);
bad_ble_script_set_keyboard_layout(app->bad_ble_script, app->keyboard_layout);
scene_manager_next_scene(app->scene_manager, BadBleSceneWork);
} else {
furi_string_set(app->file_path, BAD_BLE_APP_BASE_FOLDER);
scene_manager_next_scene(app->scene_manager, BadBleSceneFileSelect);
}
return app;
}
void bad_ble_app_free(BadBleApp* app) {
furi_assert(app);
if(app->bad_ble_script) {
bad_ble_script_close(app->bad_ble_script);
app->bad_ble_script = NULL;
}
// Views
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewWork);
bad_ble_free(app->bad_ble_view);
// Custom Widget
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewError);
widget_free(app->widget);
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewConfig);
submenu_free(app->submenu);
// Text Input
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewConfigName);
text_input_free(app->text_input);
// Byte Input
view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewConfigMac);
byte_input_free(app->byte_input);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Close records
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_DIALOGS);
furi_record_close(RECORD_BT);
bad_ble_save_settings(app);
furi_string_free(app->file_path);
furi_string_free(app->keyboard_layout);
free(app);
}
int32_t bad_ble_app(void* p) {
BadBleApp* bad_ble_app = bad_ble_app_alloc((char*)p);
view_dispatcher_run(bad_ble_app->view_dispatcher);
bad_ble_app_free(bad_ble_app);
return 0;
}

View File

@@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct BadBleApp BadBleApp;
void bad_ble_set_name(BadBleApp *app, const char* fmt, ...);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,64 @@
#pragma once
#include "bad_ble_app.h"
#include "scenes/bad_ble_scene.h"
#include "bad_ble_script.h"
#include "bad_ble_custom_event.h"
#include <gui/gui.h>
#include <bad_ble_icons.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#include "views/bad_ble_view.h"
#define BAD_BLE_APP_BASE_FOLDER ANY_PATH("BadUsb")
#define BAD_BLE_APP_PATH_LAYOUT_FOLDER BAD_BLE_APP_BASE_FOLDER "/layouts"
#define BAD_BLE_APP_SCRIPT_EXTENSION ".txt"
#define BAD_BLE_APP_LAYOUT_EXTENSION ".kl"
#define BAD_BLE_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro
#define BAD_BLE_ADV_NAME_MAX_LEN 18
typedef enum {
BadBleAppErrorNoFiles,
BadBleAppErrorCloseRpc,
} BadBleAppError;
struct BadBleApp {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs;
Widget* widget;
Submenu* submenu;
TextInput *text_input;
ByteInput *byte_input;
uint8_t mac[BAD_BLE_MAC_ADDRESS_LEN];
char name[BAD_BLE_ADV_NAME_MAX_LEN + 1];
BadBleAppError error;
FuriString* file_path;
FuriString* keyboard_layout;
BadBle* bad_ble_view;
BadBleScript* bad_ble_script;
Bt* bt;
};
typedef enum {
BadBleAppViewError,
BadBleAppViewWork,
BadBleAppViewConfig,
BadBleAppViewConfigMac,
BadBleAppViewConfigName
} BadBleAppView;

View File

@@ -0,0 +1,7 @@
typedef enum BadBleCustomEvent {
BadBleAppCustomEventTextEditResult,
BadBleAppCustomEventByteInputDone,
BadBleCustomEventErrorBack
} BadBleCustomEvent;

View File

@@ -0,0 +1,783 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <lib/toolbox/args.h>
#include <furi_hal_bt_hid.h>
#include <storage/storage.h>
#include "bad_ble_script.h"
#include <dolphin/dolphin.h>
#include <bt/bt_service/bt.h>
#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys")
#define TAG "BadBle"
#define WORKER_TAG TAG "Worker"
#define FILE_BUFFER_LEN 16
#define SCRIPT_STATE_ERROR (-1)
#define SCRIPT_STATE_END (-2)
#define SCRIPT_STATE_NEXT_LINE (-3)
#define BADBLE_ASCII_TO_KEY(script, x) \
(((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
typedef enum {
WorkerEvtToggle = (1 << 0),
WorkerEvtEnd = (1 << 1),
WorkerEvtConnect = (1 << 2),
WorkerEvtDisconnect = (1 << 3),
} WorkerEvtFlags;
typedef enum {
LevelRssi122_100,
LevelRssi99_80,
LevelRssi79_60,
LevelRssi59_40,
LevelRssi39_0,
LevelRssiNum,
} LevelRssiDelays;
const uint8_t bt_hid_delays[LevelRssiNum] = {
45, // LevelRssi122_100
41, // LevelRssi99_80
37, // LevelRssi79_60
33, // LevelRssi59_40
30, // LevelRssi39_0
};
struct BadBleScript {
BadBleState st;
FuriString* file_path;
uint32_t defdelay;
uint16_t layout[128];
FuriThread* thread;
uint8_t file_buf[FILE_BUFFER_LEN + 1];
uint8_t buf_start;
uint8_t buf_len;
bool file_end;
FuriString* line;
FuriString* line_prev;
uint32_t repeat_cnt;
File* debug_file;
Bt* bt;
};
typedef struct {
char* name;
uint16_t keycode;
} DuckyKey;
static const DuckyKey ducky_keys[] = {
{"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT},
{"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT},
{"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
{"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
{"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
{"CTRL", KEY_MOD_LEFT_CTRL},
{"CONTROL", KEY_MOD_LEFT_CTRL},
{"SHIFT", KEY_MOD_LEFT_SHIFT},
{"ALT", KEY_MOD_LEFT_ALT},
{"GUI", KEY_MOD_LEFT_GUI},
{"WINDOWS", KEY_MOD_LEFT_GUI},
{"DOWNARROW", HID_KEYBOARD_DOWN_ARROW},
{"DOWN", HID_KEYBOARD_DOWN_ARROW},
{"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
{"LEFT", HID_KEYBOARD_LEFT_ARROW},
{"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW},
{"RIGHT", HID_KEYBOARD_RIGHT_ARROW},
{"UPARROW", HID_KEYBOARD_UP_ARROW},
{"UP", HID_KEYBOARD_UP_ARROW},
{"ENTER", HID_KEYBOARD_RETURN},
{"BREAK", HID_KEYBOARD_PAUSE},
{"PAUSE", HID_KEYBOARD_PAUSE},
{"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
{"DELETE", HID_KEYBOARD_DELETE},
{"BACKSPACE", HID_KEYPAD_BACKSPACE},
{"END", HID_KEYBOARD_END},
{"ESC", HID_KEYBOARD_ESCAPE},
{"ESCAPE", HID_KEYBOARD_ESCAPE},
{"HOME", HID_KEYBOARD_HOME},
{"INSERT", HID_KEYBOARD_INSERT},
{"NUMLOCK", HID_KEYPAD_NUMLOCK},
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
{"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
{"SPACE", HID_KEYBOARD_SPACEBAR},
{"TAB", HID_KEYBOARD_TAB},
{"MENU", HID_KEYBOARD_APPLICATION},
{"APP", HID_KEYBOARD_APPLICATION},
{"F1", HID_KEYBOARD_F1},
{"F2", HID_KEYBOARD_F2},
{"F3", HID_KEYBOARD_F3},
{"F4", HID_KEYBOARD_F4},
{"F5", HID_KEYBOARD_F5},
{"F6", HID_KEYBOARD_F6},
{"F7", HID_KEYBOARD_F7},
{"F8", HID_KEYBOARD_F8},
{"F9", HID_KEYBOARD_F9},
{"F10", HID_KEYBOARD_F10},
{"F11", HID_KEYBOARD_F11},
{"F12", HID_KEYBOARD_F12},
};
static const char ducky_cmd_comment[] = {"REM"};
static const char ducky_cmd_id[] = {"ID"};
static const char ducky_cmd_delay[] = {"DELAY "};
static const char ducky_cmd_string[] = {"STRING "};
static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "};
static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "};
static const char ducky_cmd_repeat[] = {"REPEAT "};
static const char ducky_cmd_sysrq[] = {"SYSRQ "};
static const char ducky_cmd_altchar[] = {"ALTCHAR "};
static const char ducky_cmd_altstr_1[] = {"ALTSTRING "};
static const char ducky_cmd_altstr_2[] = {"ALTCODE "};
static const char ducky_cmd_lang[] = {"DUCKY_LANG"};
static const uint8_t numpad_keys[10] = {
HID_KEYPAD_0,
HID_KEYPAD_1,
HID_KEYPAD_2,
HID_KEYPAD_3,
HID_KEYPAD_4,
HID_KEYPAD_5,
HID_KEYPAD_6,
HID_KEYPAD_7,
HID_KEYPAD_8,
HID_KEYPAD_9,
};
/**
* @brief Wait until there are enough free slots in the keyboard buffer
*
* @param n_free_chars Number of free slots to wait for (and consider the buffer not full)
*/
static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t timeout) {
uint32_t start = furi_get_tick();
uint32_t timeout_ms = timeout <= -1 ? 0 : timeout;
while(furi_hal_bt_hid_kb_free_slots(n_free_chars) == false) {
furi_delay_ms(100);
if(timeout != -1 && (furi_get_tick() - start) > timeout_ms) {
break;
}
}
}
static bool ducky_get_number(const char* param, uint32_t* val) {
uint32_t value = 0;
if(sscanf(param, "%lu", &value) == 1) {
*val = value;
return true;
}
return false;
}
static uint32_t ducky_get_command_len(const char* line) {
uint32_t len = strlen(line);
for(uint32_t i = 0; i < len; i++) {
if(line[i] == ' ') return i;
}
return 0;
}
static bool ducky_is_line_end(const char chr) {
return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'));
}
static void ducky_numlock_on() {
if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) {
bt_hid_hold_while_keyboard_buffer_full(1, -1);
furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi());
furi_delay_ms(25);
furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);
}
}
static bool ducky_numpad_press(const char num) {
if((num < '0') || (num > '9')) return false;
uint16_t key = numpad_keys[num - '0'];
bt_hid_hold_while_keyboard_buffer_full(1, -1);
FURI_LOG_I(WORKER_TAG, "Pressing %c\r\n", num);
furi_hal_bt_hid_kb_press(key);
FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi());
furi_delay_ms(25);
furi_hal_bt_hid_kb_release(key);
return true;
}
static bool ducky_altchar(const char* charcode) {
uint8_t i = 0;
bool state = false;
FURI_LOG_I(WORKER_TAG, "char %s", charcode);
bt_hid_hold_while_keyboard_buffer_full(1, -1);
furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT);
while(!ducky_is_line_end(charcode[i])) {
state = ducky_numpad_press(charcode[i]);
if(state == false) break;
i++;
}
furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT);
return state;
}
static bool ducky_altstring(const char* param) {
uint32_t i = 0;
bool state = false;
while(param[i] != '\0') {
if((param[i] < ' ') || (param[i] > '~')) {
i++;
continue; // Skip non-printable chars
}
char temp_str[4];
snprintf(temp_str, 4, "%u", param[i]);
state = ducky_altchar(temp_str);
if(state == false) break;
i++;
}
return state;
}
static bool ducky_string(BadBleScript* bad_ble, const char* param) {
uint32_t i = 0;
while(param[i] != '\0') {
uint16_t keycode = BADBLE_ASCII_TO_KEY(bad_ble, param[i]);
if(keycode != HID_KEYBOARD_NONE) {
bt_hid_hold_while_keyboard_buffer_full(1, -1);
furi_hal_bt_hid_kb_press(keycode);
FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi());
furi_delay_ms(25);
furi_hal_bt_hid_kb_release(keycode);
}
i++;
}
return true;
}
static uint16_t ducky_get_keycode(BadBleScript* bad_ble, const char* param, bool accept_chars) {
for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
size_t key_cmd_len = strlen(ducky_keys[i].name);
if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
(ducky_is_line_end(param[key_cmd_len]))) {
return ducky_keys[i].keycode;
}
}
if((accept_chars) && (strlen(param) > 0)) {
return (BADBLE_ASCII_TO_KEY(bad_ble, param[0]) & 0xFF);
}
return 0;
}
static int32_t
ducky_parse_line(BadBleScript* bad_ble, FuriString* line, char* error, size_t error_len) {
uint32_t line_len = furi_string_size(line);
const char* line_tmp = furi_string_get_cstr(line);
bool state = false;
if(line_len == 0) {
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
}
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
// General commands
if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
// REM - comment line
return (0);
} else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
// ID - executed in ducky_script_preload
return (0);
} else if(strncmp(line_tmp, ducky_cmd_lang, strlen(ducky_cmd_lang)) == 0) {
// DUCKY_LANG - ignore command to retain compatibility with existing scripts
return (0);
} else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
// DELAY
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
uint32_t delay_val = 0;
state = ducky_get_number(line_tmp, &delay_val);
if((state) && (delay_val > 0)) {
return (int32_t)delay_val;
}
if(error != NULL) {
snprintf(error, error_len, "Invalid number %s", line_tmp);
}
return SCRIPT_STATE_ERROR;
} else if(
(strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
(strncmp(line_tmp, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) {
// DEFAULT_DELAY
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
state = ducky_get_number(line_tmp, &bad_ble->defdelay);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid number %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
// STRING
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
state = ducky_string(bad_ble, line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid string %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
// ALTCHAR
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
ducky_numlock_on();
state = ducky_altchar(line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid altchar %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(
(strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
(strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) {
// ALTSTRING
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
ducky_numlock_on();
state = ducky_altstring(line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid altstring %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
// REPEAT
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
state = ducky_get_number(line_tmp, &bad_ble->repeat_cnt);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid number %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
// SYSRQ
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
uint16_t key = ducky_get_keycode(bad_ble, line_tmp, true);
bt_hid_hold_while_keyboard_buffer_full(1, -1);
furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
furi_hal_bt_hid_kb_press(key);
FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi());
furi_delay_ms(25);
furi_hal_bt_hid_kb_release(key);
furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
return (0);
} else {
// Special keys + modifiers
uint16_t key = ducky_get_keycode(bad_ble, line_tmp, false);
if(key == HID_KEYBOARD_NONE) {
if(error != NULL) {
snprintf(error, error_len, "No keycode defined for %s", line_tmp);
}
return SCRIPT_STATE_ERROR;
}
if((key & 0xFF00) != 0) {
// It's a modifier key
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
key |= ducky_get_keycode(bad_ble, line_tmp, true);
}
FURI_LOG_I(WORKER_TAG, "Special key pressed %x\r\n", key);
furi_hal_bt_hid_kb_press(key);
FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi());
furi_delay_ms(25);
furi_hal_bt_hid_kb_release(key);
return (0);
}
}
static bool ducky_script_preload(BadBleScript* bad_ble, File* script_file) {
uint8_t ret = 0;
uint32_t line_len = 0;
furi_string_reset(bad_ble->line);
do {
ret = storage_file_read(script_file, bad_ble->file_buf, FILE_BUFFER_LEN);
for(uint16_t i = 0; i < ret; i++) {
if(bad_ble->file_buf[i] == '\n' && line_len > 0) {
bad_ble->st.line_nb++;
line_len = 0;
} else {
if(bad_ble->st.line_nb == 0) { // Save first line
furi_string_push_back(bad_ble->line, bad_ble->file_buf[i]);
}
line_len++;
}
}
if(storage_file_eof(script_file)) {
if(line_len > 0) {
bad_ble->st.line_nb++;
break;
}
}
} while(ret > 0);
storage_file_seek(script_file, 0, true);
furi_string_reset(bad_ble->line);
return true;
}
static int32_t ducky_script_execute_next(BadBleScript* bad_ble, File* script_file) {
int32_t delay_val = 0;
if(bad_ble->repeat_cnt > 0) {
bad_ble->repeat_cnt--;
delay_val = ducky_parse_line(
bad_ble, bad_ble->line_prev, bad_ble->st.error, sizeof(bad_ble->st.error));
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
return 0;
} else if(delay_val < 0) { // Script error
bad_ble->st.error_line = bad_ble->st.line_cur - 1;
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_ble->st.line_cur - 1U);
return SCRIPT_STATE_ERROR;
} else {
return (delay_val + bad_ble->defdelay);
}
}
furi_string_set(bad_ble->line_prev, bad_ble->line);
furi_string_reset(bad_ble->line);
while(1) {
if(bad_ble->buf_len == 0) {
bad_ble->buf_len = storage_file_read(script_file, bad_ble->file_buf, FILE_BUFFER_LEN);
if(storage_file_eof(script_file)) {
if((bad_ble->buf_len < FILE_BUFFER_LEN) && (bad_ble->file_end == false)) {
bad_ble->file_buf[bad_ble->buf_len] = '\n';
bad_ble->buf_len++;
bad_ble->file_end = true;
}
}
bad_ble->buf_start = 0;
if(bad_ble->buf_len == 0) return SCRIPT_STATE_END;
}
for(uint8_t i = bad_ble->buf_start; i < (bad_ble->buf_start + bad_ble->buf_len); i++) {
if(bad_ble->file_buf[i] == '\n' && furi_string_size(bad_ble->line) > 0) {
bad_ble->st.line_cur++;
bad_ble->buf_len = bad_ble->buf_len + bad_ble->buf_start - (i + 1);
bad_ble->buf_start = i + 1;
furi_string_trim(bad_ble->line);
delay_val = ducky_parse_line(
bad_ble, bad_ble->line, bad_ble->st.error, sizeof(bad_ble->st.error));
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
return 0;
} else if(delay_val < 0) {
bad_ble->st.error_line = bad_ble->st.line_cur;
if(delay_val == SCRIPT_STATE_NEXT_LINE) {
snprintf(
bad_ble->st.error, sizeof(bad_ble->st.error), "Forbidden empty line");
FURI_LOG_E(
WORKER_TAG, "Forbidden empty line at line %u", bad_ble->st.line_cur);
} else {
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_ble->st.line_cur);
}
return SCRIPT_STATE_ERROR;
} else {
return (delay_val + bad_ble->defdelay);
}
} else {
furi_string_push_back(bad_ble->line, bad_ble->file_buf[i]);
}
}
bad_ble->buf_len = 0;
if(bad_ble->file_end) return SCRIPT_STATE_END;
}
return 0;
}
static void bad_ble_hid_state_callback(BtStatus status, void* context) {
furi_assert(context);
BadBleScript* bad_ble = (BadBleScript*)context;
bool state = (status == BtStatusConnected);
if(state == true)
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtConnect);
else
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtDisconnect);
}
static int32_t bad_ble_worker(void* context) {
BadBleScript* bad_ble = context;
BadBleWorkerState worker_state = BadBleStateInit;
int32_t delay_val = 0;
// init ble hid
bt_disconnect(bad_ble->bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_storage_path(bad_ble->bt, HID_BT_KEYS_STORAGE_PATH);
bt_set_profile_adv_name(bad_ble->bt, "Keyboard K99");
//furi_hal_bt_set_profile_adv_name("Keyboard K99", FuriHalBtProfileHidKeyboard);
if(!bt_set_profile(bad_ble->bt, BtProfileHidKeyboard)) {
FURI_LOG_E(TAG, "Failed to switch to HID profile");
return -1;
}
furi_hal_bt_start_advertising();
FURI_LOG_I(WORKER_TAG, "Init");
File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
bad_ble->line = furi_string_alloc();
bad_ble->line_prev = furi_string_alloc();
bt_set_status_changed_callback(bad_ble->bt, bad_ble_hid_state_callback, bad_ble);
while(1) {
if(worker_state == BadBleStateInit) { // State: initialization
if(storage_file_open(
script_file,
furi_string_get_cstr(bad_ble->file_path),
FSAM_READ,
FSOM_OPEN_EXISTING)) {
if((ducky_script_preload(bad_ble, script_file)) && (bad_ble->st.line_nb > 0)) {
worker_state = BadBleStateNotConnected; // Ready to run
} else {
worker_state = BadBleStateScriptError; // Script preload error
}
} else {
FURI_LOG_E(WORKER_TAG, "File open error");
worker_state = BadBleStateFileError; // File open error
}
bad_ble->st.state = worker_state;
} else if(worker_state == BadBleStateNotConnected) { // State: ble not connected
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
FuriFlagWaitAny,
FuriWaitForever);
furi_check((flags & FuriFlagError) == 0);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) {
worker_state = BadBleStateIdle; // Ready to run
} else if(flags & WorkerEvtToggle) {
worker_state = BadBleStateWillRun; // Will run when ble is connected
}
bad_ble->st.state = worker_state;
} else if(worker_state == BadBleStateIdle) { // State: ready to start
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
FuriFlagWaitAny,
FuriWaitForever);
furi_check((flags & FuriFlagError) == 0);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtToggle) { // Start executing script
DOLPHIN_DEED(DolphinDeedBadUsbPlayScript);
delay_val = 0;
bad_ble->buf_len = 0;
bad_ble->st.line_cur = 0;
bad_ble->defdelay = 0;
bad_ble->repeat_cnt = 0;
bad_ble->file_end = false;
storage_file_seek(script_file, 0, true);
worker_state = BadBleStateRunning;
} else if(flags & WorkerEvtDisconnect) {
worker_state = BadBleStateNotConnected; // ble disconnected
}
bad_ble->st.state = worker_state;
} else if(worker_state == BadBleStateWillRun) { // State: start on connection
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
FuriFlagWaitAny,
FuriWaitForever);
furi_check((flags & FuriFlagError) == 0);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) { // Start executing script
DOLPHIN_DEED(DolphinDeedBadUsbPlayScript);
delay_val = 0;
bad_ble->buf_len = 0;
bad_ble->st.line_cur = 0;
bad_ble->defdelay = 0;
bad_ble->repeat_cnt = 0;
bad_ble->file_end = false;
storage_file_seek(script_file, 0, true);
// extra time for PC to recognize Flipper as keyboard
furi_thread_flags_wait(0, FuriFlagWaitAny, 1500);
worker_state = BadBleStateRunning;
} else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
worker_state = BadBleStateNotConnected;
}
bad_ble->st.state = worker_state;
} else if(worker_state == BadBleStateRunning) { // State: running
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur);
delay_val -= delay_cur;
if(!(flags & FuriFlagError)) {
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtToggle) {
worker_state = BadBleStateIdle; // Stop executing script
furi_hal_bt_hid_kb_release_all();
} else if(flags & WorkerEvtDisconnect) {
worker_state = BadBleStateNotConnected; // ble disconnected
furi_hal_bt_hid_kb_release_all();
}
bad_ble->st.state = worker_state;
continue;
} else if(
(flags == (unsigned)FuriFlagErrorTimeout) ||
(flags == (unsigned)FuriFlagErrorResource)) {
if(delay_val > 0) {
bad_ble->st.delay_remain--;
continue;
}
bad_ble->st.state = BadBleStateRunning;
delay_val = ducky_script_execute_next(bad_ble, script_file);
if(delay_val == SCRIPT_STATE_ERROR) { // Script error
delay_val = 0;
worker_state = BadBleStateScriptError;
bad_ble->st.state = worker_state;
} else if(delay_val == SCRIPT_STATE_END) { // End of script
delay_val = 0;
worker_state = BadBleStateIdle;
bad_ble->st.state = BadBleStateDone;
furi_hal_bt_hid_kb_release_all();
continue;
} else if(delay_val > 1000) {
bad_ble->st.state = BadBleStateDelay; // Show long delays
bad_ble->st.delay_remain = delay_val / 1000;
}
} else {
furi_check((flags & FuriFlagError) == 0);
}
} else if(
(worker_state == BadBleStateFileError) ||
(worker_state == BadBleStateScriptError)) { // State: error
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command
furi_check((flags & FuriFlagError) == 0);
if(flags & WorkerEvtEnd) {
break;
}
}
}
// release all keys
bt_hid_hold_while_keyboard_buffer_full(6, 3000);
// stop ble
bt_set_status_changed_callback(bad_ble->bt, NULL, NULL);
bt_disconnect(bad_ble->bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_default_path(bad_ble->bt);
if(!bt_set_profile(bad_ble->bt, BtProfileSerial)) {
FURI_LOG_E(TAG, "Failed to switch to Serial profile");
}
storage_file_close(script_file);
storage_file_free(script_file);
furi_string_free(bad_ble->line);
furi_string_free(bad_ble->line_prev);
FURI_LOG_I(WORKER_TAG, "End");
return 0;
}
static void bad_ble_script_set_default_keyboard_layout(BadBleScript* bad_ble) {
furi_assert(bad_ble);
memset(bad_ble->layout, HID_KEYBOARD_NONE, sizeof(bad_ble->layout));
memcpy(bad_ble->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_ble->layout)));
}
BadBleScript* bad_ble_script_open(FuriString* file_path, Bt* bt) {
furi_assert(file_path);
BadBleScript* bad_ble = malloc(sizeof(BadBleScript));
bad_ble->file_path = furi_string_alloc();
furi_string_set(bad_ble->file_path, file_path);
bad_ble_script_set_default_keyboard_layout(bad_ble);
bad_ble->st.state = BadBleStateInit;
bad_ble->st.error[0] = '\0';
bad_ble->bt = bt;
bad_ble->thread = furi_thread_alloc_ex("BadBleWorker", 2048, bad_ble_worker, bad_ble);
furi_thread_start(bad_ble->thread);
return bad_ble;
} //-V773
void bad_ble_script_close(BadBleScript* bad_ble) {
furi_assert(bad_ble);
furi_record_close(RECORD_STORAGE);
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtEnd);
furi_thread_join(bad_ble->thread);
furi_thread_free(bad_ble->thread);
furi_string_free(bad_ble->file_path);
free(bad_ble);
}
void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path) {
furi_assert(bad_ble);
if((bad_ble->st.state == BadBleStateRunning) || (bad_ble->st.state == BadBleStateDelay)) {
// do not update keyboard layout while a script is running
return;
}
File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
if(!furi_string_empty(layout_path)) {
if(storage_file_open(
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
uint16_t layout[128];
if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) {
memcpy(bad_ble->layout, layout, sizeof(layout));
}
}
storage_file_close(layout_file);
} else {
bad_ble_script_set_default_keyboard_layout(bad_ble);
}
storage_file_free(layout_file);
}
void bad_ble_script_toggle(BadBleScript* bad_ble) {
furi_assert(bad_ble);
furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtToggle);
}
BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble) {
furi_assert(bad_ble);
return &(bad_ble->st);
}

View File

@@ -0,0 +1,49 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
#include <bt/bt_service/bt_i.h>
typedef struct BadBleScript BadBleScript;
typedef enum {
BadBleStateInit,
BadBleStateNotConnected,
BadBleStateIdle,
BadBleStateWillRun,
BadBleStateRunning,
BadBleStateDelay,
BadBleStateDone,
BadBleStateScriptError,
BadBleStateFileError,
} BadBleWorkerState;
typedef struct {
BadBleWorkerState state;
uint16_t line_cur;
uint16_t line_nb;
uint32_t delay_remain;
uint16_t error_line;
char error[64];
} BadBleState;
BadBleScript* bad_ble_script_open(FuriString* file_path, Bt *bt);
void bad_ble_script_close(BadBleScript* bad_ble);
void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path);
void bad_ble_script_start(BadBleScript* bad_ble);
void bad_ble_script_stop(BadBleScript* bad_ble);
void bad_ble_script_toggle(BadBleScript* bad_ble);
BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,3 @@
#pragma once
#define BAD_BLE_SETTINGS_FILE_NAME ".BadBle.settings"

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

View File

@@ -0,0 +1,30 @@
#include "bad_ble_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const bad_ble_scene_on_enter_handlers[])(void*) = {
#include "bad_ble_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const bad_ble_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "bad_ble_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const bad_ble_scene_on_exit_handlers[])(void* context) = {
#include "bad_ble_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers bad_ble_scene_handlers = {
.on_enter_handlers = bad_ble_scene_on_enter_handlers,
.on_event_handlers = bad_ble_scene_on_event_handlers,
.on_exit_handlers = bad_ble_scene_on_exit_handlers,
.scene_num = BadBleSceneNum,
};

View File

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

View File

@@ -0,0 +1,72 @@
#include "../bad_ble_app_i.h"
#include "furi_hal_power.h"
enum SubmenuIndex {
SubmenuIndexKeyboardLayout,
SubmenuIndexAdvertisementName,
SubmenuIndexMacAddress,
};
void bad_ble_scene_config_submenu_callback(void* context, uint32_t index) {
BadBleApp* bad_ble = context;
view_dispatcher_send_custom_event(bad_ble->view_dispatcher, index);
}
void bad_ble_scene_config_on_enter(void* context) {
BadBleApp* bad_ble = context;
Submenu* submenu = bad_ble->submenu;
submenu_add_item(
submenu,
"Keyboard layout",
SubmenuIndexKeyboardLayout,
bad_ble_scene_config_submenu_callback,
bad_ble);
submenu_add_item(
submenu,
"Change adv name",
SubmenuIndexAdvertisementName,
bad_ble_scene_config_submenu_callback,
bad_ble);
submenu_add_item(
submenu,
"Change MAC address",
SubmenuIndexMacAddress,
bad_ble_scene_config_submenu_callback,
bad_ble);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(bad_ble->scene_manager, BadBleSceneConfig));
view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfig);
}
bool bad_ble_scene_config_on_event(void* context, SceneManagerEvent event) {
BadBleApp* bad_ble = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(bad_ble->scene_manager, BadBleSceneConfig, event.event);
consumed = true;
if(event.event == SubmenuIndexKeyboardLayout) {
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigLayout);
} else if (event.event == SubmenuIndexAdvertisementName) {
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigName);
} else if (event.event == SubmenuIndexMacAddress) {
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigMac);
} else {
furi_crash("Unknown key type");
}
}
return consumed;
}
void bad_ble_scene_config_on_exit(void* context) {
BadBleApp* bad_ble = context;
Submenu* submenu = bad_ble->submenu;
submenu_reset(submenu);
}

View File

@@ -0,0 +1,7 @@
ADD_SCENE(bad_ble, file_select, FileSelect)
ADD_SCENE(bad_ble, work, Work)
ADD_SCENE(bad_ble, error, Error)
ADD_SCENE(bad_ble, config, Config)
ADD_SCENE(bad_ble, config_layout, ConfigLayout)
ADD_SCENE(bad_ble, config_name, ConfigName)
ADD_SCENE(bad_ble, config_mac, ConfigMac)

View File

@@ -0,0 +1,47 @@
#include "../bad_ble_app_i.h"
#include "furi_hal_power.h"
#include <storage/storage.h>
static bool bad_ble_layout_select(BadBleApp* bad_ble) {
furi_assert(bad_ble);
FuriString* predefined_path;
predefined_path = furi_string_alloc();
if(!furi_string_empty(bad_ble->keyboard_layout)) {
furi_string_set(predefined_path, bad_ble->keyboard_layout);
} else {
furi_string_set(predefined_path, BAD_BLE_APP_PATH_LAYOUT_FOLDER);
}
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, BAD_BLE_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
// Input events and views are managed by file_browser
bool res = dialog_file_browser_show(
bad_ble->dialogs, bad_ble->keyboard_layout, predefined_path, &browser_options);
furi_string_free(predefined_path);
return res;
}
void bad_ble_scene_config_layout_on_enter(void* context) {
BadBleApp* bad_ble = context;
if(bad_ble_layout_select(bad_ble)) {
bad_ble_script_set_keyboard_layout(bad_ble->bad_ble_script, bad_ble->keyboard_layout);
}
scene_manager_previous_scene(bad_ble->scene_manager);
}
bool bad_ble_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
// BadBleApp* bad_ble = context;
return false;
}
void bad_ble_scene_config_layout_on_exit(void* context) {
UNUSED(context);
// BadBleApp* bad_ble = context;
}

View File

@@ -0,0 +1,47 @@
#include "../bad_ble_app_i.h"
#define TAG "BadBleConfigMac"
void bad_ble_scene_config_mac_byte_input_callback(void* context) {
BadBleApp* bad_ble = context;
view_dispatcher_send_custom_event(bad_ble->view_dispatcher, BadBleAppCustomEventByteInputDone);
}
void bad_ble_scene_config_mac_on_enter(void* context) {
BadBleApp* bad_ble = context;
// Setup view
ByteInput* byte_input = bad_ble->byte_input;
byte_input_set_header_text(byte_input, "Enter new MAC address");
byte_input_set_result_callback(
byte_input,
bad_ble_scene_config_mac_byte_input_callback,
NULL,
bad_ble,
bad_ble->mac,
GAP_MAC_ADDR_SIZE);
view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfigMac);
}
bool bad_ble_scene_config_mac_on_event(void* context, SceneManagerEvent event) {
BadBleApp* bad_ble = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == BadBleAppCustomEventByteInputDone) {
bt_set_profile_mac_address(bad_ble->bt, bad_ble->mac);
scene_manager_previous_scene(bad_ble->scene_manager);
consumed = true;
}
}
return consumed;
}
void bad_ble_scene_config_mac_on_exit(void* context) {
BadBleApp* bad_ble = context;
// Clear view
byte_input_set_result_callback(bad_ble->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(bad_ble->byte_input, "");
}

View File

@@ -0,0 +1,47 @@
#include "../bad_ble_app_i.h"
static void bad_ble_scene_config_name_text_input_callback(void* context) {
BadBleApp* bad_ble = context;
view_dispatcher_send_custom_event(
bad_ble->view_dispatcher, BadBleAppCustomEventTextEditResult);
}
void bad_ble_scene_config_name_on_enter(void* context) {
BadBleApp* bad_ble = context;
TextInput* text_input = bad_ble->text_input;
text_input_set_header_text(text_input, "Set BLE adv name");
text_input_set_result_callback(
text_input,
bad_ble_scene_config_name_text_input_callback,
bad_ble,
bad_ble->name,
BAD_BLE_ADV_NAME_MAX_LEN,
true);
view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfigName);
}
bool bad_ble_scene_config_name_on_event(void* context, SceneManagerEvent event) {
BadBleApp* bad_ble = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == BadBleAppCustomEventTextEditResult) {
bt_set_profile_adv_name(bad_ble->bt, bad_ble->name);
}
scene_manager_previous_scene(
bad_ble->scene_manager);
}
return consumed;
}
void bad_ble_scene_config_name_on_exit(void* context) {
BadBleApp* bad_ble = context;
TextInput* text_input = bad_ble->text_input;
text_input_reset(text_input);
}

View File

@@ -0,0 +1,79 @@
#include "../bad_ble_app_i.h"
#include "../../../settings/desktop_settings/desktop_settings_app.h"
static void
bad_ble_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
BadBleApp* app = context;
if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
view_dispatcher_send_custom_event(app->view_dispatcher, BadBleCustomEventErrorBack);
}
}
void bad_ble_scene_error_on_enter(void* context) {
BadBleApp* app = context;
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
DESKTOP_SETTINGS_LOAD(settings);
if(app->error == BadBleAppErrorNoFiles) {
widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);
widget_add_string_multiline_element(
app->widget,
81,
4,
AlignCenter,
AlignTop,
FontSecondary,
"No SD card or\napp data found.\nThis app will not\nwork without\nrequired files.");
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Back", bad_ble_scene_error_event_callback, app);
} else if(app->error == BadBleAppErrorCloseRpc) {
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
if (settings->sfw_mode) {
widget_add_string_multiline_element(
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
widget_add_string_multiline_element(
app->widget,
3,
30,
AlignLeft,
AlignTop,
FontSecondary,
"Disconnect from\nPC or phone to\nuse this function.");
}
else {
widget_add_string_multiline_element(
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!");
widget_add_string_multiline_element(
app->widget,
3,
30,
AlignLeft,
AlignTop,
FontSecondary,
"Pull out from\nPC or phone to\nuse me like this.");
}
}
view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewError);
free(settings);
}
bool bad_ble_scene_error_on_event(void* context, SceneManagerEvent event) {
BadBleApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == BadBleCustomEventErrorBack) {
view_dispatcher_stop(app->view_dispatcher);
consumed = true;
}
}
return consumed;
}
void bad_ble_scene_error_on_exit(void* context) {
BadBleApp* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,51 @@
#include "../bad_ble_app_i.h"
#include "furi_hal_power.h"
#include <storage/storage.h>
static bool bad_ble_file_select(BadBleApp* bad_ble) {
furi_assert(bad_ble);
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, BAD_BLE_APP_SCRIPT_EXTENSION, &I_badusb_10px);
browser_options.base_path = BAD_BLE_APP_BASE_FOLDER;
browser_options.skip_assets = true;
// Input events and views are managed by file_browser
bool res = dialog_file_browser_show(
bad_ble->dialogs, bad_ble->file_path, bad_ble->file_path, &browser_options);
return res;
}
void bad_ble_scene_file_select_on_enter(void* context) {
BadBleApp* bad_ble = context;
// furi_hal_ble_disable();
if(bad_ble->bad_ble_script) {
bad_ble_script_close(bad_ble->bad_ble_script);
bad_ble->bad_ble_script = NULL;
}
if(bad_ble_file_select(bad_ble)) {
bad_ble->bad_ble_script = bad_ble_script_open(bad_ble->file_path, bad_ble->bt);
bad_ble_script_set_keyboard_layout(bad_ble->bad_ble_script, bad_ble->keyboard_layout);
scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneWork);
} else {
// furi_hal_ble_enable();
view_dispatcher_stop(bad_ble->view_dispatcher);
}
}
bool bad_ble_scene_file_select_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
// BadBleApp* bad_ble = context;
return false;
}
void bad_ble_scene_file_select_on_exit(void* context) {
UNUSED(context);
// BadBleApp* bad_ble = context;
}

View File

@@ -0,0 +1,54 @@
#include "../bad_ble_script.h"
#include "../bad_ble_app_i.h"
#include "../views/bad_ble_view.h"
#include "furi_hal.h"
#include "toolbox/path.h"
void bad_ble_scene_work_button_callback(InputKey key, void* context) {
furi_assert(context);
BadBleApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, key);
}
bool bad_ble_scene_work_on_event(void* context, SceneManagerEvent event) {
BadBleApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InputKeyLeft) {
scene_manager_next_scene(app->scene_manager, BadBleSceneConfig);
consumed = true;
} else if(event.event == InputKeyOk) {
bad_ble_script_toggle(app->bad_ble_script);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeTick) {
bad_ble_set_state(app->bad_ble_view, bad_ble_script_get_state(app->bad_ble_script));
}
return consumed;
}
void bad_ble_scene_work_on_enter(void* context) {
BadBleApp* app = context;
FuriString* file_name;
file_name = furi_string_alloc();
path_extract_filename(app->file_path, file_name, true);
bad_ble_set_file_name(app->bad_ble_view, furi_string_get_cstr(file_name));
furi_string_free(file_name);
FuriString* layout;
layout = furi_string_alloc();
path_extract_filename(app->keyboard_layout, layout, true);
bad_ble_set_layout(app->bad_ble_view, furi_string_get_cstr(layout));
furi_string_free(layout);
bad_ble_set_state(app->bad_ble_view, bad_ble_script_get_state(app->bad_ble_script));
bad_ble_set_button_callback(app->bad_ble_view, bad_ble_scene_work_button_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewWork);
}
void bad_ble_scene_work_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,24 @@
import os
import re
def analyze_and_replace(directory):
# Recursively search for .c and .h files in the given directory
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith(".c") or file.endswith(".h"):
# Read the contents of the file
with open(os.path.join(root, file), "r") as f:
contents = f.read()
# Replace all occurrences of "BadUsb" and "bad_usb" with "BadBle" and "bad_ble"
contents = contents.replace("usb", "ble")
contents = contents.replace("USB", "BLE")
contents = contents.replace("Usb", "Ble")
# Write the modified contents back to the file
with open(os.path.join(root, file), "w") as f:
f.write(contents)
# Test the function with a sample directory
analyze_and_replace(".")

View File

@@ -0,0 +1,237 @@
#include "bad_ble_view.h"
#include "../bad_ble_script.h"
#include <toolbox/path.h>
#include <gui/elements.h>
#include <bad_ble_icons.h>
#include "../../../settings/desktop_settings/desktop_settings_app.h"
#define MAX_NAME_LEN 64
struct BadBle {
View* view;
BadBleButtonCallback callback;
void* context;
};
typedef struct {
char file_name[MAX_NAME_LEN];
char layout[MAX_NAME_LEN];
BadBleState state;
uint8_t anim_frame;
} BadBleModel;
static void bad_ble_draw_callback(Canvas* canvas, void* _model) {
BadBleModel* model = _model;
FuriString* disp_str;
disp_str = furi_string_alloc_set(model->file_name);
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
DesktopSettings* settings = malloc(sizeof(DesktopSettings));
DESKTOP_SETTINGS_LOAD(settings);
if(strlen(model->layout) == 0) {
furi_string_set(disp_str, "(default)");
} else {
furi_string_reset(disp_str);
furi_string_push_back(disp_str, '(');
for(size_t i = 0; i < strlen(model->layout); i++)
furi_string_push_back(disp_str, model->layout[i]);
furi_string_push_back(disp_str, ')');
}
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_draw_str(
canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
if((model->state.state == BadBleStateIdle) || (model->state.state == BadBleStateDone) ||
(model->state.state == BadBleStateNotConnected)) {
if (settings->sfw_mode) {
elements_button_center(canvas, "Start");
}
else {
elements_button_center(canvas, "Cum");
}
} else if((model->state.state == BadBleStateRunning) || (model->state.state == BadBleStateDelay)) {
elements_button_center(canvas, "Stop");
} else if(model->state.state == BadBleStateWillRun) {
elements_button_center(canvas, "Cancel");
}
if((model->state.state == BadBleStateNotConnected) ||
(model->state.state == BadBleStateIdle) || (model->state.state == BadBleStateDone)) {
elements_button_left(canvas, "Config");
}
if(model->state.state == BadBleStateNotConnected) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
if (settings->sfw_mode) {
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect me");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to a computer");
}
else {
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy");
}
} else if(model->state.state == BadBleStateWillRun) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
if (settings->sfw_mode) {
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
}
else {
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum");
}
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
} else if(model->state.state == BadBleStateFileError) {
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
} else if(model->state.state == BadBleStateScriptError) {
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
canvas_set_font(canvas, FontSecondary);
furi_string_printf(disp_str, "line %u", model->state.error_line);
canvas_draw_str_aligned(
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error);
} else if(model->state.state == BadBleStateIdle) {
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0");
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
} else if(model->state.state == BadBleStateRunning) {
if(model->anim_frame == 0) {
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
} else {
canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21);
}
canvas_set_font(canvas, FontBigNumbers);
furi_string_printf(
disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
canvas_draw_str_aligned(
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
} else if(model->state.state == BadBleStateDone) {
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100");
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
} else if(model->state.state == BadBleStateDelay) {
if(model->anim_frame == 0) {
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
} else {
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
}
canvas_set_font(canvas, FontBigNumbers);
furi_string_printf(
disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
canvas_draw_str_aligned(
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
canvas_set_font(canvas, FontSecondary);
furi_string_printf(disp_str, "delay %lus", model->state.delay_remain);
canvas_draw_str_aligned(
canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
} else {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
}
furi_string_free(disp_str);
free(settings);
}
static bool bad_ble_input_callback(InputEvent* event, void* context) {
furi_assert(context);
BadBle* bad_ble = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) {
consumed = true;
furi_assert(bad_ble->callback);
bad_ble->callback(event->key, bad_ble->context);
}
}
return consumed;
}
BadBle* bad_ble_alloc() {
BadBle* bad_ble = malloc(sizeof(BadBle));
bad_ble->view = view_alloc();
view_allocate_model(bad_ble->view, ViewModelTypeLocking, sizeof(BadBleModel));
view_set_context(bad_ble->view, bad_ble);
view_set_draw_callback(bad_ble->view, bad_ble_draw_callback);
view_set_input_callback(bad_ble->view, bad_ble_input_callback);
return bad_ble;
}
void bad_ble_free(BadBle* bad_ble) {
furi_assert(bad_ble);
view_free(bad_ble->view);
free(bad_ble);
}
View* bad_ble_get_view(BadBle* bad_ble) {
furi_assert(bad_ble);
return bad_ble->view;
}
void bad_ble_set_button_callback(BadBle* bad_ble, BadBleButtonCallback callback, void* context) {
furi_assert(bad_ble);
furi_assert(callback);
with_view_model(
bad_ble->view,
BadBleModel * model,
{
UNUSED(model);
bad_ble->callback = callback;
bad_ble->context = context;
},
true);
}
void bad_ble_set_file_name(BadBle* bad_ble, const char* name) {
furi_assert(name);
with_view_model(
bad_ble->view,
BadBleModel * model,
{ strlcpy(model->file_name, name, MAX_NAME_LEN); },
true);
}
void bad_ble_set_layout(BadBle* bad_ble, const char* layout) {
furi_assert(layout);
with_view_model(
bad_ble->view,
BadBleModel * model,
{ strlcpy(model->layout, layout, MAX_NAME_LEN); },
true);
}
void bad_ble_set_state(BadBle* bad_ble, BadBleState* st) {
furi_assert(st);
with_view_model(
bad_ble->view,
BadBleModel * model,
{
memcpy(&(model->state), st, sizeof(BadBleState));
model->anim_frame ^= 1;
},
true);
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <gui/view.h>
#include "../bad_ble_script.h"
typedef struct BadBle BadBle;
typedef void (*BadBleButtonCallback)(InputKey key, void* context);
BadBle* bad_ble_alloc();
void bad_ble_free(BadBle* bad_ble);
View* bad_ble_get_view(BadBle* bad_ble);
void bad_ble_set_button_callback(BadBle* bad_ble, BadBleButtonCallback callback, void* context);
void bad_ble_set_file_name(BadBle* bad_ble, const char* name);
void bad_ble_set_layout(BadBle* bad_ble, const char* layout);
void bad_ble_set_state(BadBle* bad_ble, BadBleState* st);

View File

@@ -1,3 +1,6 @@
#ifndef __GPIO_SCENE_START_H__
#define __GPIO_SCENE_START_H__
#include "../gpio_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
@@ -35,7 +38,8 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind
}
}
static void gpio_scene_start_var_list_change_callback(VariableItem* item) {
static void
gpio_scene_start_var_list_change_callback(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
@@ -117,3 +121,5 @@ void gpio_scene_start_on_exit(void* context) {
GpioApp* app = context;
variable_item_list_reset(app->var_item_list);
}
#endif // __GPIO_SCENE_START_H__

View File

@@ -384,6 +384,64 @@ static void bt_close_connection(Bt* bt) {
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
}
static void bt_restart(Bt *bt) {
if (bt->profile == BtProfileHidKeyboard) {
furi_hal_bt_change_app(FuriHalBtProfileHidKeyboard, bt_on_gap_event_callback, bt);
} else {
furi_hal_bt_change_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt);
}
furi_hal_bt_start_advertising();
}
void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...) {
furi_assert(bt);
furi_assert(fmt);
char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];
va_list args;
va_start(args, fmt);
vsnprintf(name, sizeof(name), fmt, args);
va_end(args);
if (bt->profile == BtProfileHidKeyboard) {
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, name);
} else {
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileSerial, name);
}
bt_restart(bt);
}
const char *bt_get_profile_adv_name(Bt *bt) {
furi_assert(bt);
if (bt->profile == BtProfileHidKeyboard) {
return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard);
} else {
return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileSerial);
}
}
void bt_set_profile_mac_address(Bt *bt, const uint8_t mac[6]) {
furi_assert(bt);
furi_assert(mac);
if (bt->profile == BtProfileHidKeyboard) {
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac);
} else {
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileSerial, mac);
}
bt_restart(bt);
}
const uint8_t *bt_get_profile_mac_address(Bt *bt) {
furi_assert(bt);
if (bt->profile == BtProfileHidKeyboard) {
return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard);
} else {
return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileSerial);
}
}
int32_t bt_srv(void* p) {
UNUSED(p);
Bt* bt = bt_alloc();

View File

@@ -35,6 +35,14 @@ typedef void (*BtStatusChangedCallback)(BtStatus status, void* context);
*/
bool bt_set_profile(Bt* bt, BtProfile profile);
void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...);
const char *bt_get_profile_adv_name(Bt *bt);
void bt_set_profile_mac_address(Bt *bt, const uint8_t mac[6]);
const uint8_t *bt_get_profile_mac_address(Bt *bt);
/** Disconnect from Central
*
* @param bt Bt instance

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,12.3,,
Version,v,12.7,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,,
@@ -571,9 +571,13 @@ Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t
Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t"
Function,+,bt_disconnect,void,Bt*
Function,+,bt_forget_bonded_devices,void,Bt*
Function,?,bt_get_profile_adv_name,const char*,Bt*
Function,?,bt_get_profile_mac_address,const uint8_t*,Bt*
Function,+,bt_keys_storage_set_default_path,void,Bt*
Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*"
Function,+,bt_set_profile,_Bool,"Bt*, BtProfile"
Function,?,bt_set_profile,_Bool,"Bt*, BtProfile"
Function,?,bt_set_profile_adv_name,void,"Bt*, const char*, ..."
Function,?,bt_set_profile_mac_address,void,"Bt*, const uint8_t[6]"
Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*"
Function,+,buffered_file_stream_alloc,Stream*,Storage*
Function,+,buffered_file_stream_close,_Bool,Stream*
@@ -603,15 +607,7 @@ Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, B
Function,-,bzero,void,"void*, size_t"
Function,-,calloc,void*,"size_t, size_t"
Function,+,canvas_clear,void,Canvas*
<<<<<<< HEAD
Function,+,canvas_commit,void,Canvas*
=======
<<<<<<< HEAD
Function,-,canvas_commit,void,Canvas*
=======
Function,+,canvas_commit,void,Canvas*
>>>>>>> b11b9f1b3 (Gui: Direct Draw API (#2215))
>>>>>>> e04f2b4ce (api_symbols)
Function,+,canvas_current_font_height,uint8_t,Canvas*
Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*"
Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
@@ -640,15 +636,7 @@ Function,+,canvas_glyph_width,uint8_t,"Canvas*, char"
Function,+,canvas_height,uint8_t,Canvas*
Function,-,canvas_init,Canvas*,
Function,+,canvas_invert_color,void,Canvas*
<<<<<<< HEAD
Function,+,canvas_reset,void,Canvas*
=======
<<<<<<< HEAD
Function,-,canvas_reset,void,Canvas*
=======
Function,+,canvas_reset,void,Canvas*
>>>>>>> b11b9f1b3 (Gui: Direct Draw API (#2215))
>>>>>>> e04f2b4ce (api_symbols)
Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool"
Function,+,canvas_set_color,void,"Canvas*, Color"
Function,+,canvas_set_font,void,"Canvas*, Font"
@@ -1012,12 +1000,15 @@ Function,+,furi_hal_bt_clear_white_list,_Bool,
Function,+,furi_hal_bt_dump_state,void,FuriString*
Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode
Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*"
Function,?,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile
Function,?,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile
Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack,
Function,+,furi_hal_bt_get_rssi,float,
Function,+,furi_hal_bt_get_transmitted_packets,uint32_t,
Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t
Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t
Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool,
Function,?,furi_hal_bt_hid_kb_free_slots,_Bool,uint8_t
Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t
Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t
Function,+,furi_hal_bt_hid_kb_release_all,_Bool,
@@ -1044,6 +1035,8 @@ Function,+,furi_hal_bt_serial_start,void,
Function,+,furi_hal_bt_serial_stop,void,
Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t"
Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*"
Function,?,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + 8 + ( 8 + 1 ) )]"
Function,?,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]"
Function,+,furi_hal_bt_start_advertising,void,
Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*"
Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t"
1 entry status name type params
2 Version + v 12.3 12.7
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
571 Function - bsearch void* const void*, const void*, size_t, size_t, __compar_fn_t
572 Function + bt_disconnect void Bt*
573 Function + bt_forget_bonded_devices void Bt*
574 Function ? bt_get_profile_adv_name const char* Bt*
575 Function ? bt_get_profile_mac_address const uint8_t* Bt*
576 Function + bt_keys_storage_set_default_path void Bt*
577 Function + bt_keys_storage_set_storage_path void Bt*, const char*
578 Function + ? bt_set_profile _Bool Bt*, BtProfile
579 Function ? bt_set_profile_adv_name void Bt*, const char*, ...
580 Function ? bt_set_profile_mac_address void Bt*, const uint8_t[6]
581 Function + bt_set_status_changed_callback void Bt*, BtStatusChangedCallback, void*
582 Function + buffered_file_stream_alloc Stream* Storage*
583 Function + buffered_file_stream_close _Bool Stream*
607 Function - bzero void void*, size_t
608 Function - calloc void* size_t, size_t
609 Function + canvas_clear void Canvas*
<<<<<<< HEAD
Function + canvas_commit void Canvas*
=======
<<<<<<< HEAD
610 Function - canvas_commit void Canvas*
=======
Function + canvas_commit void Canvas*
>>>>>>> b11b9f1b3 (Gui: Direct Draw API (#2215))
>>>>>>> e04f2b4ce (api_symbols)
611 Function + canvas_current_font_height uint8_t Canvas*
612 Function + canvas_draw_bitmap void Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*
613 Function + canvas_draw_box void Canvas*, uint8_t, uint8_t, uint8_t, uint8_t
636 Function + canvas_height uint8_t Canvas*
637 Function - canvas_init Canvas*
638 Function + canvas_invert_color void Canvas*
<<<<<<< HEAD
Function + canvas_reset void Canvas*
=======
<<<<<<< HEAD
639 Function - canvas_reset void Canvas*
=======
Function + canvas_reset void Canvas*
>>>>>>> b11b9f1b3 (Gui: Direct Draw API (#2215))
>>>>>>> e04f2b4ce (api_symbols)
640 Function + canvas_set_bitmap_mode void Canvas*, _Bool
641 Function + canvas_set_color void Canvas*, Color
642 Function + canvas_set_font void Canvas*, Font
1000 Function + furi_hal_bt_dump_state void FuriString*
1001 Function + furi_hal_bt_ensure_c2_mode _Bool BleGlueC2Mode
1002 Function + furi_hal_bt_get_key_storage_buff void uint8_t**, uint16_t*
1003 Function ? furi_hal_bt_get_profile_adv_name const char* FuriHalBtProfile
1004 Function ? furi_hal_bt_get_profile_mac_addr const uint8_t* FuriHalBtProfile
1005 Function + furi_hal_bt_get_radio_stack FuriHalBtStack
1006 Function + furi_hal_bt_get_rssi float
1007 Function + furi_hal_bt_get_transmitted_packets uint32_t
1008 Function + furi_hal_bt_hid_consumer_key_press _Bool uint16_t
1009 Function + furi_hal_bt_hid_consumer_key_release _Bool uint16_t
1010 Function + furi_hal_bt_hid_consumer_key_release_all _Bool
1011 Function ? furi_hal_bt_hid_kb_free_slots _Bool uint8_t
1012 Function + furi_hal_bt_hid_kb_press _Bool uint16_t
1013 Function + furi_hal_bt_hid_kb_release _Bool uint16_t
1014 Function + furi_hal_bt_hid_kb_release_all _Bool
1035 Function + furi_hal_bt_serial_stop void
1036 Function + furi_hal_bt_serial_tx _Bool uint8_t*, uint16_t
1037 Function + furi_hal_bt_set_key_storage_change_callback void BleGlueKeyStorageChangedCallback, void*
1038 Function ? furi_hal_bt_set_profile_adv_name void FuriHalBtProfile, const char[( 1 + 8 + ( 8 + 1 ) )]
1039 Function ? furi_hal_bt_set_profile_mac_addr void FuriHalBtProfile, const uint8_t[( 6 )]
1040 Function + furi_hal_bt_start_advertising void
1041 Function + furi_hal_bt_start_app _Bool FuriHalBtProfile, GapEventCallback, void*
1042 Function + furi_hal_bt_start_packet_rx void uint8_t, uint8_t

View File

@@ -313,7 +313,7 @@ static void gap_init_svc(Gap* gap) {
// Initialize GATT interface
aci_gatt_init();
// Initialize GAP interface
// Skip fist symbol AD_TYPE_COMPLETE_LOCAL_NAME
// Skip first symbol AD_TYPE_COMPLETE_LOCAL_NAME
char* name = gap->service.adv_name + 1;
aci_gap_init(
GAP_PERIPHERAL_ROLE,
@@ -362,7 +362,7 @@ static void gap_init_svc(Gap* gap) {
keypress_supported,
CFG_ENCRYPTION_KEY_SIZE_MIN,
CFG_ENCRYPTION_KEY_SIZE_MAX,
CFG_USED_FIXED_PIN,
CFG_USED_FIXED_PIN, // 0x0 for no pin
0,
PUBLIC_ADDR);
// Configure whitelist
@@ -482,6 +482,10 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
gap->advertise_timer = furi_timer_alloc(gap_advetise_timer_callback, FuriTimerTypeOnce, NULL);
// Initialization of GATT & GAP layer
gap->service.adv_name = config->adv_name;
FURI_LOG_I(TAG, "Advertising name: %s", &(gap->service.adv_name[1]));
FURI_LOG_I(TAG, "MAC @ : %02X:%02X:%02X:%02X:%02X:%02X",
config->mac_address[0], config->mac_address[1], config->mac_address[2],
config->mac_address[3], config->mac_address[4], config->mac_address[5]);
gap_init_svc(gap);
// Initialization of the BLE Services
SVCCTL_Init();

View File

@@ -54,27 +54,25 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = {
},
},
},
[FuriHalBtProfileHidKeyboard] =
{
.start = furi_hal_bt_hid_start,
.stop = furi_hal_bt_hid_stop,
.config =
{
.adv_service_uuid = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
.appearance_char = GAP_APPEARANCE_KEYBOARD,
.bonding_mode = true,
.pairing_method = GapPairingPinCodeVerifyYesNo,
.mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
.conn_param =
{
.conn_int_min = 0x18, // 30 ms
.conn_int_max = 0x24, // 45 ms
.slave_latency = 0,
.supervisor_timeout = 0,
},
},
},
};
[FuriHalBtProfileHidKeyboard] = {
.start = furi_hal_bt_hid_start,
.stop = furi_hal_bt_hid_stop,
.config =
{
.adv_service_uuid = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
.appearance_char = GAP_APPEARANCE_KEYBOARD,
.bonding_mode = true,
.pairing_method = GapPairingPinCodeVerifyYesNo,
.mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
.conn_param =
{
.conn_int_min = 0x18, // 30 ms
.conn_int_max = 0x24, // 45 ms
.slave_latency = 0,
.supervisor_timeout = 0,
},
},
}};
FuriHalBtProfileConfig* current_profile = NULL;
void furi_hal_bt_init() {
@@ -201,26 +199,33 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb,
FURI_LOG_E(TAG, "Can't start Ble App - unsupported radio stack");
break;
}
// Set mac address
memcpy(
profile_config[profile].config.mac_address,
furi_hal_version_get_ble_mac(),
sizeof(profile_config[profile].config.mac_address));
// Set advertise name
strlcpy(
profile_config[profile].config.adv_name,
furi_hal_version_get_ble_local_device_name_ptr(),
FURI_HAL_VERSION_DEVICE_NAME_LENGTH);
// Configure GAP
GapConfig* config = &profile_config[profile].config;
if (strlen(&(profile_config[profile].config.adv_name[1])) == 0) {
// Set advertise name
strlcpy(
profile_config[profile].config.adv_name,
furi_hal_version_get_ble_local_device_name_ptr(),
FURI_HAL_VERSION_DEVICE_NAME_LENGTH);
}
// Configure GAP
if(profile == FuriHalBtProfileSerial) {
// Set mac address
memcpy(
profile_config[profile].config.mac_address,
furi_hal_version_get_ble_mac(),
sizeof(profile_config[profile].config.mac_address));
config->adv_service_uuid |= furi_hal_version_get_hw_color();
} else if(profile == FuriHalBtProfileHidKeyboard) {
// Change MAC address for HID profile
config->mac_address[2]++;
// Change name Flipper -> Flipper Remote
const char* clicker_str = "Flipper Remote";
memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str));
uint8_t default_mac[GAP_MAC_ADDR_SIZE] = FURI_HAL_BT_DEFAULT_MAC_ADDR;
if(memcmp(config->mac_address, default_mac, 6) == 0) {
config->mac_address[2]++;
}
// Change name Flipper -> Control
if(strlen(&config->adv_name[1]) == 0) {
const char* clicker_str = "Control";
memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str));
}
}
if(!gap_init(config, event_cb, context)) {
gap_thread_stop();
@@ -444,3 +449,32 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) {
FURI_LOG_E(TAG, "Failed to switch C2 mode: %d", fw_start_res);
return false;
}
void furi_hal_bt_set_profile_adv_name(FuriHalBtProfile profile, const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]) {
furi_assert(profile < FuriHalBtProfileNumber);
furi_assert(name);
memcpy(&(profile_config[profile].config.adv_name[1]),
name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH);
}
const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile) {
furi_assert(profile < FuriHalBtProfileNumber);
return &(profile_config[profile].config.adv_name[1]);
}
void furi_hal_bt_set_profile_mac_addr(
FuriHalBtProfile profile,
const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) {
furi_assert(profile < FuriHalBtProfileNumber);
furi_assert(mac_addr);
memcpy(profile_config[profile].config.mac_address, mac_addr, GAP_MAC_ADDR_SIZE);
}
const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile) {
furi_assert(profile < FuriHalBtProfileNumber);
return profile_config[profile].config.mac_address;
}

View File

@@ -138,10 +138,11 @@ void furi_hal_bt_hid_start() {
if(!hid_svc_is_started()) {
hid_svc_start();
}
// Configure HID Keyboard
kb_report = malloc(sizeof(FuriHalBtHidKbReport));
mouse_report = malloc(sizeof(FuriHalBtHidMouseReport));
consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport));
// Configure Report Map characteristic
hid_svc_update_report_map(
furi_hal_bt_hid_report_map_data, sizeof(furi_hal_bt_hid_report_map_data));
@@ -180,17 +181,30 @@ void furi_hal_bt_hid_stop() {
bool furi_hal_bt_hid_kb_press(uint16_t button) {
furi_assert(kb_report);
for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) {
uint8_t i;
for(i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) {
if(kb_report->key[i] == 0) {
kb_report->key[i] = button & 0xFF;
break;
}
}
if(i == FURI_HAL_BT_HID_KB_MAX_KEYS) {
return false;
}
kb_report->mods |= (button >> 8);
return hid_svc_update_input_report(
ReportNumberKeyboard, (uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport));
}
bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots) {
furi_assert(kb_report);
for(uint8_t i = 0; n_empty_slots > 0 && i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) {
if(kb_report->key[i] == 0)
n_empty_slots--;
}
return (n_empty_slots == 0);
}
bool furi_hal_bt_hid_kb_release(uint16_t button) {
furi_assert(kb_report);
for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) {

View File

@@ -224,6 +224,14 @@ uint32_t furi_hal_bt_get_transmitted_packets();
*/
bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode);
void furi_hal_bt_set_profile_adv_name(FuriHalBtProfile profile, const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]);
const char *furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile);
void furi_hal_bt_set_profile_mac_addr(FuriHalBtProfile profile, const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]);
const uint8_t *furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile);
#ifdef __cplusplus
}
#endif

View File

@@ -86,6 +86,15 @@ bool furi_hal_bt_hid_consumer_key_release(uint16_t button);
*/
bool furi_hal_bt_hid_consumer_key_release_all();
/**
* @brief Check if keyboard buffer has free slots
*
* @param n_emptry_slots number of empty slots in buffer to consider buffer is not full
*
* @return true if there is enough free slots in buffer
*/
bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots);
#ifdef __cplusplus
}
#endif