Adds BLE adv name & mac changing effective
18
applications/main/bad_ble/application.fam
Normal 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"],
|
||||
)
|
||||
195
applications/main/bad_ble/bad_ble_app.c
Normal 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;
|
||||
}
|
||||
13
applications/main/bad_ble/bad_ble_app.h
Normal 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
|
||||
64
applications/main/bad_ble/bad_ble_app_i.h
Normal 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;
|
||||
7
applications/main/bad_ble/bad_ble_custom_event.h
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
typedef enum BadBleCustomEvent {
|
||||
BadBleAppCustomEventTextEditResult,
|
||||
BadBleAppCustomEventByteInputDone,
|
||||
BadBleCustomEventErrorBack
|
||||
} BadBleCustomEvent;
|
||||
783
applications/main/bad_ble/bad_ble_script.c
Normal 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);
|
||||
}
|
||||
49
applications/main/bad_ble/bad_ble_script.h
Normal 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
|
||||
3
applications/main/bad_ble/bad_ble_settings_filename.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define BAD_BLE_SETTINGS_FILE_NAME ".BadBle.settings"
|
||||
BIN
applications/main/bad_ble/badusb_10px.png
Normal file
|
After Width: | Height: | Size: 576 B |
BIN
applications/main/bad_ble/images/ActiveConnection_50x64.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
applications/main/bad_ble/images/Clock_18x18.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
applications/main/bad_ble/images/Error_18x18.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
applications/main/bad_ble/images/EviSmile1_18x21.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/main/bad_ble/images/EviSmile2_18x21.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/main/bad_ble/images/EviWaiting1_18x21.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
applications/main/bad_ble/images/EviWaiting2_18x21.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
applications/main/bad_ble/images/Percent_10x14.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/main/bad_ble/images/SDQuestion_35x43.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
applications/main/bad_ble/images/Smile_18x18.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
applications/main/bad_ble/images/UsbTree_48x22.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/main/bad_ble/images/badusb_10px.png
Normal file
|
After Width: | Height: | Size: 576 B |
BIN
applications/main/bad_ble/images/keyboard_10px.png
Normal file
|
After Width: | Height: | Size: 147 B |
30
applications/main/bad_ble/scenes/bad_ble_scene.c
Normal 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,
|
||||
};
|
||||
29
applications/main/bad_ble/scenes/bad_ble_scene.h
Normal 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
|
||||
72
applications/main/bad_ble/scenes/bad_ble_scene_config.c
Normal 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);
|
||||
}
|
||||
7
applications/main/bad_ble/scenes/bad_ble_scene_config.h
Normal 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)
|
||||
@@ -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;
|
||||
}
|
||||
47
applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c
Normal 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, "");
|
||||
}
|
||||
47
applications/main/bad_ble/scenes/bad_ble_scene_config_name.c
Normal 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);
|
||||
}
|
||||
79
applications/main/bad_ble/scenes/bad_ble_scene_error.c
Normal 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);
|
||||
}
|
||||
51
applications/main/bad_ble/scenes/bad_ble_scene_file_select.c
Normal 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;
|
||||
}
|
||||
54
applications/main/bad_ble/scenes/bad_ble_scene_work.c
Normal 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);
|
||||
}
|
||||
24
applications/main/bad_ble/switch_ble.py
Normal 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(".")
|
||||
237
applications/main/bad_ble/views/bad_ble_view.c
Normal 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);
|
||||
}
|
||||
21
applications/main/bad_ble/views/bad_ble_view.h
Normal 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);
|
||||
@@ -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__
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||