[FL-1930] USB HID (#751)
* [FL-1930] USB HID keyboard test * [FL-1930] HID mouse demo app * [FL-1930] BadUSB: RubberDucky script parser. BadUSB test app Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
parent
1db29eaea8
commit
e6642b332c
@ -35,6 +35,8 @@ extern int32_t subghz_app(void* p);
|
|||||||
extern int32_t vibro_test_app(void* p);
|
extern int32_t vibro_test_app(void* p);
|
||||||
extern int32_t bt_debug_app(void* p);
|
extern int32_t bt_debug_app(void* p);
|
||||||
extern int32_t usb_test_app(void* p);
|
extern int32_t usb_test_app(void* p);
|
||||||
|
extern int32_t usb_mouse_app(void* p);
|
||||||
|
extern int32_t bad_usb_app(void* p);
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
extern int32_t music_player_app(void* p);
|
extern int32_t music_player_app(void* p);
|
||||||
@ -225,6 +227,14 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
|
|||||||
{.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = &A_Plugins_14},
|
{.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef APP_USB_MOUSE
|
||||||
|
{.app = usb_mouse_app, .name = "USB Mouse demo", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef APP_BAD_USB
|
||||||
|
{.app = bad_usb_app, .name = "Bad USB test", .stack_size = 2048, .icon = &A_Plugins_14},
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef APP_IRDA_MONITOR
|
#ifdef APP_IRDA_MONITOR
|
||||||
{.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = &A_Plugins_14},
|
{.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||||
#endif
|
#endif
|
||||||
|
@ -44,6 +44,8 @@ APP_KEYPAD_TEST = 1
|
|||||||
APP_SD_TEST = 1
|
APP_SD_TEST = 1
|
||||||
APP_VIBRO_DEMO = 1
|
APP_VIBRO_DEMO = 1
|
||||||
APP_USB_TEST = 1
|
APP_USB_TEST = 1
|
||||||
|
APP_USB_MOUSE = 1
|
||||||
|
APP_BAD_USB = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
@ -127,8 +129,21 @@ ifeq ($(APP_USB_TEST), 1)
|
|||||||
CFLAGS += -DAPP_USB_TEST
|
CFLAGS += -DAPP_USB_TEST
|
||||||
SRV_INPUT = 1
|
SRV_INPUT = 1
|
||||||
SRV_GUI = 1
|
SRV_GUI = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
APP_USB_MOUSE ?= 0
|
||||||
|
ifeq ($(APP_USB_MOUSE), 1)
|
||||||
|
CFLAGS += -DAPP_USB_MOUSE
|
||||||
|
SRV_INPUT = 1
|
||||||
|
SRV_GUI = 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
APP_BAD_USB ?= 0
|
||||||
|
ifeq ($(APP_BAD_USB), 1)
|
||||||
|
CFLAGS += -DAPP_BAD_USB
|
||||||
|
SRV_INPUT = 1
|
||||||
|
SRV_GUI = 1
|
||||||
|
endif
|
||||||
|
|
||||||
APP_KEYPAD_TEST ?= 0
|
APP_KEYPAD_TEST ?= 0
|
||||||
ifeq ($(APP_KEYPAD_TEST), 1)
|
ifeq ($(APP_KEYPAD_TEST), 1)
|
||||||
|
364
applications/debug_tools/bad_usb.c
Normal file
364
applications/debug_tools/bad_usb.c
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <furi-hal.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <lib/toolbox/args.h>
|
||||||
|
#include <furi-hal-usb-hid.h>
|
||||||
|
#include <storage/storage.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
EventTypeInput,
|
||||||
|
EventTypeWorkerState,
|
||||||
|
} EventType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WorkerStateDone,
|
||||||
|
WorkerStateNoFile,
|
||||||
|
WorkerStateScriptError,
|
||||||
|
WorkerStateDisconnected,
|
||||||
|
} WorkerState;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AppStateWait,
|
||||||
|
AppStateRunning,
|
||||||
|
AppStateError,
|
||||||
|
AppStateExit,
|
||||||
|
} AppState;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WorkerCmdStart = (1 << 0),
|
||||||
|
WorkerCmdStop = (1 << 1),
|
||||||
|
} WorkerCommandFlags;
|
||||||
|
|
||||||
|
// Event message from worker
|
||||||
|
typedef struct {
|
||||||
|
WorkerState state;
|
||||||
|
uint16_t line;
|
||||||
|
} BadUsbWorkerState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
InputEvent input;
|
||||||
|
BadUsbWorkerState worker;
|
||||||
|
};
|
||||||
|
EventType type;
|
||||||
|
} BadUsbEvent;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t defdelay;
|
||||||
|
char msg_text[32];
|
||||||
|
osThreadAttr_t thread_attr;
|
||||||
|
osThreadId_t thread;
|
||||||
|
osMessageQueueId_t event_queue;
|
||||||
|
} BadUsbParams;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* name;
|
||||||
|
uint16_t keycode;
|
||||||
|
} DuckyKey;
|
||||||
|
|
||||||
|
static const DuckyKey ducky_keys[] = {
|
||||||
|
{"CTRL", KEY_MOD_LEFT_CTRL},
|
||||||
|
{"CONTROL", KEY_MOD_LEFT_CTRL},
|
||||||
|
{"SHIFT", KEY_MOD_LEFT_SHIFT},
|
||||||
|
{"ALT", KEY_MOD_LEFT_ALT},
|
||||||
|
{"GUI", KEY_MOD_LEFT_GUI},
|
||||||
|
{"WINDOWS", KEY_MOD_LEFT_GUI},
|
||||||
|
|
||||||
|
{"DOWNARROW", KEY_DOWN_ARROW},
|
||||||
|
{"DOWN", KEY_DOWN_ARROW},
|
||||||
|
{"LEFTARROW", KEY_LEFT_ARROW},
|
||||||
|
{"LEFT", KEY_LEFT_ARROW},
|
||||||
|
{"RIGHTARROW", KEY_RIGHT_ARROW},
|
||||||
|
{"RIGHT", KEY_RIGHT_ARROW},
|
||||||
|
{"UPARROW", KEY_UP_ARROW},
|
||||||
|
{"UP", KEY_UP_ARROW},
|
||||||
|
|
||||||
|
{"ENTER", KEY_ENTER},
|
||||||
|
{"BREAK", KEY_PAUSE},
|
||||||
|
{"PAUSE", KEY_PAUSE},
|
||||||
|
{"CAPSLOCK", KEY_CAPS_LOCK},
|
||||||
|
{"DELETE", KEY_DELETE},
|
||||||
|
{"BACKSPACE", KEY_BACKSPACE},
|
||||||
|
{"END", KEY_END},
|
||||||
|
{"ESC", KEY_ESC},
|
||||||
|
{"ESCAPE", KEY_ESC},
|
||||||
|
{"HOME", KEY_HOME},
|
||||||
|
{"INSERT", KEY_INSERT},
|
||||||
|
{"NUMLOCK", KEY_NUM_LOCK},
|
||||||
|
{"PAGEUP", KEY_PAGE_UP},
|
||||||
|
{"PAGEDOWN", KEY_PAGE_DOWN},
|
||||||
|
{"PRINTSCREEN", KEY_PRINT},
|
||||||
|
{"SCROLLOCK", KEY_SCROLL_LOCK},
|
||||||
|
{"SPACE", KEY_SPACE},
|
||||||
|
{"TAB", KEY_TAB},
|
||||||
|
{"MENU", KEY_APPLICATION},
|
||||||
|
{"APP", KEY_APPLICATION},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char ducky_cmd_comment[] = {"REM"};
|
||||||
|
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 bool ducky_get_delay_val(char* param, uint32_t* val) {
|
||||||
|
uint32_t delay_val = 0;
|
||||||
|
if(sscanf(param, "%lu", &delay_val) == 1) {
|
||||||
|
*val = delay_val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ducky_string(char* param) {
|
||||||
|
uint32_t i = 0;
|
||||||
|
while(param[i] != '\0') {
|
||||||
|
furi_hal_hid_kb_press(HID_ASCII_TO_KEY(param[i]));
|
||||||
|
furi_hal_hid_kb_release(HID_ASCII_TO_KEY(param[i]));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t ducky_get_keycode(char* param, bool accept_chars) {
|
||||||
|
for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
|
||||||
|
if(strncmp(param, ducky_keys[i].name, strlen(ducky_keys[i].name)) == 0)
|
||||||
|
return ducky_keys[i].keycode;
|
||||||
|
}
|
||||||
|
if((accept_chars) && (strlen(param) > 0)) {
|
||||||
|
return (HID_ASCII_TO_KEY(param[0]) & 0xFF);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ducky_parse_line(string_t line, BadUsbParams* app) {
|
||||||
|
//uint32_t line_len = string_size(line);
|
||||||
|
char* line_t = (char*)string_get_cstr(line);
|
||||||
|
bool state = false;
|
||||||
|
|
||||||
|
// General commands
|
||||||
|
if(strncmp(line_t, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
|
||||||
|
// REM - comment line
|
||||||
|
return true;
|
||||||
|
} else if(strncmp(line_t, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
|
||||||
|
// DELAY
|
||||||
|
line_t = &line_t[args_get_first_word_length(line) + 1];
|
||||||
|
uint32_t delay_val = 0;
|
||||||
|
state = ducky_get_delay_val(line_t, &delay_val);
|
||||||
|
if((state) && (delay_val > 0)) {
|
||||||
|
// Using ThreadFlagsWait as delay function allows exiting task on WorkerCmdStop command
|
||||||
|
if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, delay_val) ==
|
||||||
|
WorkerCmdStop)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
} else if(
|
||||||
|
(strncmp(line_t, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
|
||||||
|
(strncmp(line_t, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) {
|
||||||
|
// DEFAULT_DELAY
|
||||||
|
line_t = &line_t[args_get_first_word_length(line) + 1];
|
||||||
|
return ducky_get_delay_val(line_t, &app->defdelay);
|
||||||
|
} else if(strncmp(line_t, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
||||||
|
// STRING
|
||||||
|
if(app->defdelay > 0) {
|
||||||
|
if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, app->defdelay) ==
|
||||||
|
WorkerCmdStop)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
line_t = &line_t[args_get_first_word_length(line) + 1];
|
||||||
|
return ducky_string(line_t);
|
||||||
|
} else {
|
||||||
|
// Special keys + modifiers
|
||||||
|
uint16_t key = ducky_get_keycode(line_t, false);
|
||||||
|
if(key == KEY_NONE) return false;
|
||||||
|
if((key & 0xFF00) != 0) {
|
||||||
|
// It's a modifier key
|
||||||
|
line_t = &line_t[args_get_first_word_length(line) + 1];
|
||||||
|
key |= ducky_get_keycode(line_t, true);
|
||||||
|
}
|
||||||
|
if(app->defdelay > 0) {
|
||||||
|
if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, app->defdelay) ==
|
||||||
|
WorkerCmdStop)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
furi_hal_hid_kb_press(key);
|
||||||
|
furi_hal_hid_kb_release(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void badusb_worker(void* context) {
|
||||||
|
BadUsbParams* app = context;
|
||||||
|
FURI_LOG_I("BadUSB worker", "Init");
|
||||||
|
File* script_file = storage_file_alloc(furi_record_open("storage"));
|
||||||
|
BadUsbEvent evt;
|
||||||
|
string_t line;
|
||||||
|
uint32_t line_cnt = 0;
|
||||||
|
string_init(line);
|
||||||
|
if(storage_file_open(script_file, "/ext/badusb.txt", FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
|
char buffer[16];
|
||||||
|
uint16_t ret;
|
||||||
|
uint32_t flags =
|
||||||
|
osThreadFlagsWait(WorkerCmdStart | WorkerCmdStop, osFlagsWaitAny, osWaitForever);
|
||||||
|
if(flags & WorkerCmdStart) {
|
||||||
|
FURI_LOG_I("BadUSB worker", "Start");
|
||||||
|
do {
|
||||||
|
ret = storage_file_read(script_file, buffer, 16);
|
||||||
|
for(uint16_t i = 0; i < ret; i++) {
|
||||||
|
if(buffer[i] == '\n' && string_size(line) > 0) {
|
||||||
|
line_cnt++;
|
||||||
|
if(ducky_parse_line(line, app) == false) {
|
||||||
|
ret = 0;
|
||||||
|
FURI_LOG_E("BadUSB worker", "Unknown command at line %lu", line_cnt);
|
||||||
|
evt.type = EventTypeWorkerState;
|
||||||
|
evt.worker.state = WorkerStateScriptError;
|
||||||
|
evt.worker.line = line_cnt;
|
||||||
|
osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
flags = osThreadFlagsGet();
|
||||||
|
if(flags == WorkerCmdStop) {
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
string_clean(line);
|
||||||
|
} else {
|
||||||
|
string_push_back(line, buffer[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(ret > 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E("BadUSB worker", "Script file open error");
|
||||||
|
evt.type = EventTypeWorkerState;
|
||||||
|
evt.worker.state = WorkerStateNoFile;
|
||||||
|
osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
|
||||||
|
}
|
||||||
|
string_clean(line);
|
||||||
|
string_clear(line);
|
||||||
|
|
||||||
|
furi_hal_hid_kb_release_all();
|
||||||
|
storage_file_close(script_file);
|
||||||
|
storage_file_free(script_file);
|
||||||
|
|
||||||
|
FURI_LOG_I("BadUSB worker", "End");
|
||||||
|
evt.type = EventTypeWorkerState;
|
||||||
|
evt.worker.state = WorkerStateDone;
|
||||||
|
osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever);
|
||||||
|
|
||||||
|
osThreadExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bad_usb_render_callback(Canvas* canvas, void* ctx) {
|
||||||
|
BadUsbParams* app = (BadUsbParams*)ctx;
|
||||||
|
|
||||||
|
canvas_clear(canvas);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 0, 10, "Bad USB test");
|
||||||
|
|
||||||
|
if(strlen(app->msg_text) > 0) {
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 0, 62, app->msg_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bad_usb_input_callback(InputEvent* input_event, void* ctx) {
|
||||||
|
osMessageQueueId_t event_queue = ctx;
|
||||||
|
|
||||||
|
BadUsbEvent event;
|
||||||
|
event.type = EventTypeInput;
|
||||||
|
event.input = *input_event;
|
||||||
|
osMessageQueuePut(event_queue, &event, 0, osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bad_usb_app(void* p) {
|
||||||
|
BadUsbParams* app = furi_alloc(sizeof(BadUsbParams));
|
||||||
|
app->event_queue = osMessageQueueNew(8, sizeof(BadUsbEvent), NULL);
|
||||||
|
furi_check(app->event_queue);
|
||||||
|
ViewPort* view_port = view_port_alloc();
|
||||||
|
|
||||||
|
UsbMode usb_mode_prev = furi_hal_usb_get_config();
|
||||||
|
furi_hal_usb_set_config(UsbModeHid);
|
||||||
|
|
||||||
|
view_port_draw_callback_set(view_port, bad_usb_render_callback, app);
|
||||||
|
view_port_input_callback_set(view_port, bad_usb_input_callback, app->event_queue);
|
||||||
|
|
||||||
|
// Open GUI and register view_port
|
||||||
|
Gui* gui = furi_record_open("gui");
|
||||||
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
|
app->thread = NULL;
|
||||||
|
app->thread_attr.name = "bad_usb_worker";
|
||||||
|
app->thread_attr.stack_size = 2048;
|
||||||
|
app->thread = osThreadNew(badusb_worker, app, &app->thread_attr);
|
||||||
|
bool worker_running = true;
|
||||||
|
AppState app_state = AppStateWait;
|
||||||
|
snprintf(app->msg_text, sizeof(app->msg_text), "Press [OK] to start");
|
||||||
|
view_port_update(view_port);
|
||||||
|
|
||||||
|
BadUsbEvent event;
|
||||||
|
while(1) {
|
||||||
|
osStatus_t event_status = osMessageQueueGet(app->event_queue, &event, NULL, osWaitForever);
|
||||||
|
|
||||||
|
if(event_status == osOK) {
|
||||||
|
if(event.type == EventTypeInput) {
|
||||||
|
if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
|
||||||
|
if(worker_running) {
|
||||||
|
osThreadFlagsSet(app->thread, WorkerCmdStop);
|
||||||
|
app_state = AppStateExit;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.input.type == InputTypeShort && event.input.key == InputKeyOk) {
|
||||||
|
if(worker_running) {
|
||||||
|
app_state = AppStateRunning;
|
||||||
|
osThreadFlagsSet(app->thread, WorkerCmdStart);
|
||||||
|
snprintf(app->msg_text, sizeof(app->msg_text), "Running...");
|
||||||
|
view_port_update(view_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(event.type == EventTypeWorkerState) {
|
||||||
|
FURI_LOG_I("BadUSB app", "ev: %d", event.worker.state);
|
||||||
|
if(event.worker.state == WorkerStateDone) {
|
||||||
|
worker_running = false;
|
||||||
|
if(app_state == AppStateExit)
|
||||||
|
break;
|
||||||
|
else if(app_state == AppStateRunning) {
|
||||||
|
//done
|
||||||
|
app->thread = osThreadNew(badusb_worker, app, &app->thread_attr);
|
||||||
|
worker_running = true;
|
||||||
|
app_state = AppStateWait;
|
||||||
|
snprintf(app->msg_text, sizeof(app->msg_text), "Press [OK] to start");
|
||||||
|
view_port_update(view_port);
|
||||||
|
}
|
||||||
|
} else if(event.worker.state == WorkerStateNoFile) {
|
||||||
|
app_state = AppStateError;
|
||||||
|
snprintf(app->msg_text, sizeof(app->msg_text), "File not found!");
|
||||||
|
view_port_update(view_port);
|
||||||
|
} else if(event.worker.state == WorkerStateScriptError) {
|
||||||
|
app_state = AppStateError;
|
||||||
|
snprintf(
|
||||||
|
app->msg_text,
|
||||||
|
sizeof(app->msg_text),
|
||||||
|
"Error at line %u",
|
||||||
|
event.worker.line);
|
||||||
|
view_port_update(view_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
furi_hal_usb_set_config(usb_mode_prev);
|
||||||
|
|
||||||
|
// remove & free all stuff created by app
|
||||||
|
gui_remove_view_port(gui, view_port);
|
||||||
|
view_port_free(view_port);
|
||||||
|
|
||||||
|
osMessageQueueDelete(app->event_queue);
|
||||||
|
free(app);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
121
applications/debug_tools/usb_mouse.c
Normal file
121
applications/debug_tools/usb_mouse.c
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <furi-hal.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
|
||||||
|
#define MOUSE_MOVE_SHORT 5
|
||||||
|
#define MOUSE_MOVE_LONG 20
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
EventTypeInput,
|
||||||
|
} EventType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
InputEvent input;
|
||||||
|
};
|
||||||
|
EventType type;
|
||||||
|
} UsbMouseEvent;
|
||||||
|
|
||||||
|
static void usb_mouse_render_callback(Canvas* canvas, void* ctx) {
|
||||||
|
canvas_clear(canvas);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 0, 10, "USB Mouse demo");
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 0, 63, "Hold [back] to exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_mouse_input_callback(InputEvent* input_event, void* ctx) {
|
||||||
|
osMessageQueueId_t event_queue = ctx;
|
||||||
|
|
||||||
|
UsbMouseEvent event;
|
||||||
|
event.type = EventTypeInput;
|
||||||
|
event.input = *input_event;
|
||||||
|
osMessageQueuePut(event_queue, &event, 0, osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t usb_mouse_app(void* p) {
|
||||||
|
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(UsbMouseEvent), NULL);
|
||||||
|
furi_check(event_queue);
|
||||||
|
ViewPort* view_port = view_port_alloc();
|
||||||
|
|
||||||
|
UsbMode usb_mode_prev = furi_hal_usb_get_config();
|
||||||
|
furi_hal_usb_set_config(UsbModeHid);
|
||||||
|
|
||||||
|
view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL);
|
||||||
|
view_port_input_callback_set(view_port, usb_mouse_input_callback, event_queue);
|
||||||
|
|
||||||
|
// Open GUI and register view_port
|
||||||
|
Gui* gui = furi_record_open("gui");
|
||||||
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
|
UsbMouseEvent event;
|
||||||
|
while(1) {
|
||||||
|
osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, osWaitForever);
|
||||||
|
|
||||||
|
if(event_status == osOK) {
|
||||||
|
if(event.type == EventTypeInput) {
|
||||||
|
if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
|
||||||
|
furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT);
|
||||||
|
furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.input.key == InputKeyOk) {
|
||||||
|
if(event.input.type == InputTypePress) {
|
||||||
|
furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT);
|
||||||
|
} else if(event.input.type == InputTypeRelease) {
|
||||||
|
furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.input.key == InputKeyRight) {
|
||||||
|
if(event.input.type == InputTypePress) {
|
||||||
|
furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0);
|
||||||
|
} else if(event.input.type == InputTypeRepeat) {
|
||||||
|
furi_hal_hid_mouse_move(MOUSE_MOVE_LONG, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.input.key == InputKeyLeft) {
|
||||||
|
if(event.input.type == InputTypePress) {
|
||||||
|
furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0);
|
||||||
|
} else if(event.input.type == InputTypeRepeat) {
|
||||||
|
furi_hal_hid_mouse_move(-MOUSE_MOVE_LONG, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.input.key == InputKeyDown) {
|
||||||
|
if(event.input.type == InputTypePress) {
|
||||||
|
furi_hal_hid_mouse_move(0, MOUSE_MOVE_SHORT);
|
||||||
|
} else if(event.input.type == InputTypeRepeat) {
|
||||||
|
furi_hal_hid_mouse_move(0, MOUSE_MOVE_LONG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.input.key == InputKeyUp) {
|
||||||
|
if(event.input.type == InputTypePress) {
|
||||||
|
furi_hal_hid_mouse_move(0, -MOUSE_MOVE_SHORT);
|
||||||
|
} else if(event.input.type == InputTypeRepeat) {
|
||||||
|
furi_hal_hid_mouse_move(0, -MOUSE_MOVE_LONG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view_port_update(view_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_hal_usb_set_config(usb_mode_prev);
|
||||||
|
|
||||||
|
// remove & free all stuff created by app
|
||||||
|
gui_remove_view_port(gui, view_port);
|
||||||
|
view_port_free(view_port);
|
||||||
|
osMessageQueueDelete(event_queue);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/modules/submenu.h>
|
#include <gui/modules/submenu.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
|
#include <cmsis_os.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
|
@ -6,10 +6,13 @@
|
|||||||
#include "usb_hid.h"
|
#include "usb_hid.h"
|
||||||
#include "hid_usage_desktop.h"
|
#include "hid_usage_desktop.h"
|
||||||
#include "hid_usage_button.h"
|
#include "hid_usage_button.h"
|
||||||
|
#include "hid_usage_keyboard.h"
|
||||||
|
|
||||||
#define HID_RIN_EP 0x81
|
#define HID_RIN_EP 0x81
|
||||||
#define HID_RIN_SZ 0x10
|
#define HID_RIN_SZ 0x10
|
||||||
|
|
||||||
|
#define HID_KB_MAX_KEYS 6
|
||||||
|
|
||||||
struct HidIadDescriptor {
|
struct HidIadDescriptor {
|
||||||
struct usb_iad_descriptor hid_iad;
|
struct usb_iad_descriptor hid_iad;
|
||||||
struct usb_interface_descriptor hid;
|
struct usb_interface_descriptor hid;
|
||||||
@ -22,31 +25,63 @@ struct HidConfigDescriptor {
|
|||||||
struct HidIadDescriptor iad_0;
|
struct HidIadDescriptor iad_0;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
/* HID mouse report desscriptor. 2 axis 5 buttons */
|
enum HidReportId {
|
||||||
|
ReportIdKeyboard = 1,
|
||||||
|
ReportIdMouse = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* HID report: keyboard+mouse */
|
||||||
static const uint8_t hid_report_desc[] = {
|
static const uint8_t hid_report_desc[] = {
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
|
HID_USAGE(HID_DESKTOP_KEYBOARD),
|
||||||
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
|
HID_REPORT_ID(ReportIdKeyboard),
|
||||||
|
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
||||||
|
HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
|
||||||
|
HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(1),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_REPORT_COUNT(8),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_COUNT(1),
|
||||||
|
HID_REPORT_SIZE(8),
|
||||||
|
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_COUNT(6),
|
||||||
|
HID_REPORT_SIZE(8),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(101),
|
||||||
|
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
||||||
|
HID_USAGE_MINIMUM(0),
|
||||||
|
HID_USAGE_MAXIMUM(101),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
|
||||||
|
HID_END_COLLECTION,
|
||||||
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
HID_USAGE(HID_DESKTOP_MOUSE),
|
HID_USAGE(HID_DESKTOP_MOUSE),
|
||||||
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
HID_USAGE(HID_DESKTOP_POINTER),
|
HID_USAGE(HID_DESKTOP_POINTER),
|
||||||
HID_COLLECTION(HID_PHYSICAL_COLLECTION),
|
HID_COLLECTION(HID_PHYSICAL_COLLECTION),
|
||||||
|
HID_REPORT_ID(ReportIdMouse),
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_BUTTON),
|
||||||
|
HID_USAGE_MINIMUM(1),
|
||||||
|
HID_USAGE_MAXIMUM(3),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(1),
|
||||||
|
HID_REPORT_COUNT(3),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_REPORT_COUNT(5),
|
||||||
|
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
HID_USAGE(HID_DESKTOP_X),
|
HID_USAGE(HID_DESKTOP_X),
|
||||||
HID_USAGE(HID_DESKTOP_Y),
|
HID_USAGE(HID_DESKTOP_Y),
|
||||||
|
HID_USAGE(HID_DESKTOP_WHEEL),
|
||||||
HID_LOGICAL_MINIMUM(-127),
|
HID_LOGICAL_MINIMUM(-127),
|
||||||
HID_LOGICAL_MAXIMUM(127),
|
HID_LOGICAL_MAXIMUM(127),
|
||||||
HID_REPORT_SIZE(8),
|
HID_REPORT_SIZE(8),
|
||||||
HID_REPORT_COUNT(2),
|
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE ),
|
|
||||||
HID_USAGE_PAGE(HID_PAGE_BUTTON),
|
|
||||||
HID_USAGE_MINIMUM(1),
|
|
||||||
HID_USAGE_MAXIMUM(5),
|
|
||||||
HID_LOGICAL_MINIMUM(0),
|
|
||||||
HID_LOGICAL_MAXIMUM(1),
|
|
||||||
HID_REPORT_SIZE(1),
|
|
||||||
HID_REPORT_COUNT(5),
|
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE ),
|
|
||||||
HID_REPORT_SIZE(1),
|
|
||||||
HID_REPORT_COUNT(3),
|
HID_REPORT_COUNT(3),
|
||||||
HID_INPUT(HID_IOF_CONSTANT),
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
|
||||||
HID_END_COLLECTION,
|
HID_END_COLLECTION,
|
||||||
HID_END_COLLECTION,
|
HID_END_COLLECTION,
|
||||||
};
|
};
|
||||||
@ -122,25 +157,102 @@ static const struct HidConfigDescriptor hid_cfg_desc = {
|
|||||||
.bEndpointAddress = HID_RIN_EP,
|
.bEndpointAddress = HID_RIN_EP,
|
||||||
.bmAttributes = USB_EPTYPE_INTERRUPT,
|
.bmAttributes = USB_EPTYPE_INTERRUPT,
|
||||||
.wMaxPacketSize = HID_RIN_SZ,
|
.wMaxPacketSize = HID_RIN_SZ,
|
||||||
.bInterval = 50,
|
.bInterval = 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct {
|
struct HidReportMouse {
|
||||||
int8_t x;
|
uint8_t report_id;
|
||||||
int8_t y;
|
uint8_t btn;
|
||||||
uint8_t buttons;
|
int8_t x;
|
||||||
} __attribute__((packed)) hid_report_data;
|
int8_t y;
|
||||||
|
int8_t wheel;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct HidReportKB {
|
||||||
|
uint8_t report_id;
|
||||||
|
uint8_t mods;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t btn[HID_KB_MAX_KEYS];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static struct HidReport {
|
||||||
|
struct HidReportKB keyboard;
|
||||||
|
struct HidReportMouse mouse;
|
||||||
|
} __attribute__((packed)) hid_report;
|
||||||
|
|
||||||
static void hid_init(usbd_device* dev, struct UsbInterface* intf);
|
static void hid_init(usbd_device* dev, struct UsbInterface* intf);
|
||||||
static void hid_deinit(usbd_device *dev);
|
static void hid_deinit(usbd_device *dev);
|
||||||
static void hid_on_wakeup(usbd_device *dev);
|
static void hid_on_wakeup(usbd_device *dev);
|
||||||
static void hid_on_suspend(usbd_device *dev);
|
static void hid_on_suspend(usbd_device *dev);
|
||||||
|
|
||||||
|
static bool hid_send_report(uint8_t report_id);
|
||||||
static usbd_respond hid_ep_config (usbd_device *dev, uint8_t cfg);
|
static usbd_respond hid_ep_config (usbd_device *dev, uint8_t cfg);
|
||||||
static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
|
static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
|
||||||
static usbd_device* usb_dev;
|
static usbd_device* usb_dev;
|
||||||
|
static osSemaphoreId_t hid_semaphore = NULL;
|
||||||
|
static bool hid_connected = false;
|
||||||
|
|
||||||
|
bool furi_hal_hid_is_connected() {
|
||||||
|
return hid_connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_kb_press(uint16_t button) {
|
||||||
|
for (uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
||||||
|
if (hid_report.keyboard.btn[key_nb] == 0) {
|
||||||
|
hid_report.keyboard.btn[key_nb] = button & 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hid_report.keyboard.mods |= (button >> 8);
|
||||||
|
return hid_send_report(ReportIdKeyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_kb_release(uint16_t button) {
|
||||||
|
for (uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
||||||
|
if (hid_report.keyboard.btn[key_nb] == (button & 0xFF)) {
|
||||||
|
hid_report.keyboard.btn[key_nb] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hid_report.keyboard.mods &= ~(button >> 8);
|
||||||
|
return hid_send_report(ReportIdKeyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_kb_release_all() {
|
||||||
|
for (uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
||||||
|
hid_report.keyboard.btn[key_nb] = 0;
|
||||||
|
}
|
||||||
|
hid_report.keyboard.mods = 0;
|
||||||
|
return hid_send_report(ReportIdKeyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_mouse_move(int8_t dx, int8_t dy) {
|
||||||
|
hid_report.mouse.x = dx;
|
||||||
|
hid_report.mouse.y = dy;
|
||||||
|
bool state = hid_send_report(ReportIdMouse);
|
||||||
|
hid_report.mouse.x = 0;
|
||||||
|
hid_report.mouse.y = 0;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_mouse_press(uint8_t button) {
|
||||||
|
hid_report.mouse.btn |= button;
|
||||||
|
return hid_send_report(ReportIdMouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_mouse_release(uint8_t button) {
|
||||||
|
hid_report.mouse.btn &= ~button;
|
||||||
|
return hid_send_report(ReportIdMouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_mouse_scroll(int8_t delta) {
|
||||||
|
hid_report.mouse.wheel = delta;
|
||||||
|
bool state = hid_send_report(ReportIdMouse);
|
||||||
|
hid_report.mouse.wheel = 0;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
struct UsbInterface usb_hid = {
|
struct UsbInterface usb_hid = {
|
||||||
.init = hid_init,
|
.init = hid_init,
|
||||||
@ -158,7 +270,11 @@ struct UsbInterface usb_hid = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void hid_init(usbd_device* dev, struct UsbInterface* intf) {
|
static void hid_init(usbd_device* dev, struct UsbInterface* intf) {
|
||||||
|
if (hid_semaphore == NULL)
|
||||||
|
hid_semaphore = osSemaphoreNew(1, 1, NULL);
|
||||||
usb_dev = dev;
|
usb_dev = dev;
|
||||||
|
hid_report.keyboard.report_id = ReportIdKeyboard;
|
||||||
|
hid_report.mouse.report_id = ReportIdMouse;
|
||||||
|
|
||||||
usbd_reg_config(dev, hid_ep_config);
|
usbd_reg_config(dev, hid_ep_config);
|
||||||
usbd_reg_control(dev, hid_control);
|
usbd_reg_control(dev, hid_control);
|
||||||
@ -172,41 +288,34 @@ static void hid_deinit(usbd_device *dev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void hid_on_wakeup(usbd_device *dev) {
|
static void hid_on_wakeup(usbd_device *dev) {
|
||||||
|
hid_connected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hid_on_suspend(usbd_device *dev) {
|
static void hid_on_suspend(usbd_device *dev) {
|
||||||
|
if (hid_connected == true) {
|
||||||
|
hid_connected = false;
|
||||||
|
osSemaphoreRelease(hid_semaphore);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HID mouse IN endpoint callback */
|
static bool hid_send_report(uint8_t report_id)
|
||||||
static void hid_mouse_move(usbd_device *dev, uint8_t event, uint8_t ep) {
|
{
|
||||||
static uint8_t t = 0;
|
if ((hid_semaphore == NULL) || (hid_connected == false))
|
||||||
if (t < 0x10) {
|
return false;
|
||||||
hid_report_data.x = 1;
|
|
||||||
hid_report_data.y = 0;
|
furi_check(osSemaphoreAcquire(hid_semaphore, osWaitForever) == osOK);
|
||||||
} else if (t < 0x20) {
|
if (hid_connected == true) {
|
||||||
hid_report_data.x = 1;
|
if (report_id == ReportIdKeyboard)
|
||||||
hid_report_data.y = 1;
|
usbd_ep_write(usb_dev, HID_RIN_EP, &hid_report.keyboard, sizeof(hid_report.keyboard));
|
||||||
} else if (t < 0x30) {
|
else
|
||||||
hid_report_data.x = 0;
|
usbd_ep_write(usb_dev, HID_RIN_EP, &hid_report.mouse, sizeof(hid_report.mouse));
|
||||||
hid_report_data.y = 1;
|
return true;
|
||||||
} else if (t < 0x40) {
|
|
||||||
hid_report_data.x = -1;
|
|
||||||
hid_report_data.y = 1;
|
|
||||||
} else if (t < 0x50) {
|
|
||||||
hid_report_data.x = -1;
|
|
||||||
hid_report_data.y = 0;
|
|
||||||
} else if (t < 0x60) {
|
|
||||||
hid_report_data.x = -1;
|
|
||||||
hid_report_data.y = -1;
|
|
||||||
} else if (t < 0x70) {
|
|
||||||
hid_report_data.x = 0;
|
|
||||||
hid_report_data.y = -1;
|
|
||||||
} else {
|
|
||||||
hid_report_data.x = 1;
|
|
||||||
hid_report_data.y = -1;
|
|
||||||
}
|
}
|
||||||
t = (t + 1) & 0x7F;
|
return false;
|
||||||
usbd_ep_write(dev, ep, &hid_report_data, sizeof(hid_report_data));
|
}
|
||||||
|
|
||||||
|
static void hid_ep_callback(usbd_device *dev, uint8_t event, uint8_t ep) {
|
||||||
|
osSemaphoreRelease(hid_semaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure endpoints */
|
/* Configure endpoints */
|
||||||
@ -220,7 +329,7 @@ static usbd_respond hid_ep_config (usbd_device *dev, uint8_t cfg) {
|
|||||||
case 1:
|
case 1:
|
||||||
/* configuring device */
|
/* configuring device */
|
||||||
usbd_ep_config(dev, HID_RIN_EP, USB_EPTYPE_INTERRUPT, HID_RIN_SZ);
|
usbd_ep_config(dev, HID_RIN_EP, USB_EPTYPE_INTERRUPT, HID_RIN_SZ);
|
||||||
usbd_reg_endpoint(dev, HID_RIN_EP, hid_mouse_move);
|
usbd_reg_endpoint(dev, HID_RIN_EP, hid_ep_callback);
|
||||||
usbd_ep_write(dev, HID_RIN_EP, 0, 0);
|
usbd_ep_write(dev, HID_RIN_EP, 0, 0);
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
default:
|
default:
|
||||||
@ -237,8 +346,8 @@ static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_ca
|
|||||||
case USB_HID_SETIDLE:
|
case USB_HID_SETIDLE:
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
case USB_HID_GETREPORT:
|
case USB_HID_GETREPORT:
|
||||||
dev->status.data_ptr = &hid_report_data;
|
dev->status.data_ptr = &hid_report;
|
||||||
dev->status.data_count = sizeof(hid_report_data);
|
dev->status.data_count = sizeof(hid_report);
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
default:
|
default:
|
||||||
return usbd_fail;
|
return usbd_fail;
|
||||||
|
@ -89,6 +89,10 @@ void furi_hal_usb_set_config(UsbMode new_mode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UsbMode furi_hal_usb_get_config() {
|
||||||
|
return usb_config.mode_cur;
|
||||||
|
}
|
||||||
|
|
||||||
void furi_hal_usb_disable() {
|
void furi_hal_usb_disable() {
|
||||||
if (usb_config.enabled) {
|
if (usb_config.enabled) {
|
||||||
susp_evt(&udev, 0, 0);
|
susp_evt(&udev, 0, 0);
|
||||||
|
@ -6,10 +6,13 @@
|
|||||||
#include "usb_hid.h"
|
#include "usb_hid.h"
|
||||||
#include "hid_usage_desktop.h"
|
#include "hid_usage_desktop.h"
|
||||||
#include "hid_usage_button.h"
|
#include "hid_usage_button.h"
|
||||||
|
#include "hid_usage_keyboard.h"
|
||||||
|
|
||||||
#define HID_RIN_EP 0x81
|
#define HID_RIN_EP 0x81
|
||||||
#define HID_RIN_SZ 0x10
|
#define HID_RIN_SZ 0x10
|
||||||
|
|
||||||
|
#define HID_KB_MAX_KEYS 6
|
||||||
|
|
||||||
struct HidIadDescriptor {
|
struct HidIadDescriptor {
|
||||||
struct usb_iad_descriptor hid_iad;
|
struct usb_iad_descriptor hid_iad;
|
||||||
struct usb_interface_descriptor hid;
|
struct usb_interface_descriptor hid;
|
||||||
@ -22,31 +25,63 @@ struct HidConfigDescriptor {
|
|||||||
struct HidIadDescriptor iad_0;
|
struct HidIadDescriptor iad_0;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
/* HID mouse report desscriptor. 2 axis 5 buttons */
|
enum HidReportId {
|
||||||
|
ReportIdKeyboard = 1,
|
||||||
|
ReportIdMouse = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* HID report: keyboard+mouse */
|
||||||
static const uint8_t hid_report_desc[] = {
|
static const uint8_t hid_report_desc[] = {
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
|
HID_USAGE(HID_DESKTOP_KEYBOARD),
|
||||||
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
|
HID_REPORT_ID(ReportIdKeyboard),
|
||||||
|
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
||||||
|
HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
|
||||||
|
HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(1),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_REPORT_COUNT(8),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_COUNT(1),
|
||||||
|
HID_REPORT_SIZE(8),
|
||||||
|
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_COUNT(6),
|
||||||
|
HID_REPORT_SIZE(8),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(101),
|
||||||
|
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
||||||
|
HID_USAGE_MINIMUM(0),
|
||||||
|
HID_USAGE_MAXIMUM(101),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
|
||||||
|
HID_END_COLLECTION,
|
||||||
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
HID_USAGE(HID_DESKTOP_MOUSE),
|
HID_USAGE(HID_DESKTOP_MOUSE),
|
||||||
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
HID_USAGE(HID_DESKTOP_POINTER),
|
HID_USAGE(HID_DESKTOP_POINTER),
|
||||||
HID_COLLECTION(HID_PHYSICAL_COLLECTION),
|
HID_COLLECTION(HID_PHYSICAL_COLLECTION),
|
||||||
|
HID_REPORT_ID(ReportIdMouse),
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_BUTTON),
|
||||||
|
HID_USAGE_MINIMUM(1),
|
||||||
|
HID_USAGE_MAXIMUM(3),
|
||||||
|
HID_LOGICAL_MINIMUM(0),
|
||||||
|
HID_LOGICAL_MAXIMUM(1),
|
||||||
|
HID_REPORT_COUNT(3),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_REPORT_SIZE(1),
|
||||||
|
HID_REPORT_COUNT(5),
|
||||||
|
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
HID_USAGE(HID_DESKTOP_X),
|
HID_USAGE(HID_DESKTOP_X),
|
||||||
HID_USAGE(HID_DESKTOP_Y),
|
HID_USAGE(HID_DESKTOP_Y),
|
||||||
|
HID_USAGE(HID_DESKTOP_WHEEL),
|
||||||
HID_LOGICAL_MINIMUM(-127),
|
HID_LOGICAL_MINIMUM(-127),
|
||||||
HID_LOGICAL_MAXIMUM(127),
|
HID_LOGICAL_MAXIMUM(127),
|
||||||
HID_REPORT_SIZE(8),
|
HID_REPORT_SIZE(8),
|
||||||
HID_REPORT_COUNT(2),
|
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE ),
|
|
||||||
HID_USAGE_PAGE(HID_PAGE_BUTTON),
|
|
||||||
HID_USAGE_MINIMUM(1),
|
|
||||||
HID_USAGE_MAXIMUM(5),
|
|
||||||
HID_LOGICAL_MINIMUM(0),
|
|
||||||
HID_LOGICAL_MAXIMUM(1),
|
|
||||||
HID_REPORT_SIZE(1),
|
|
||||||
HID_REPORT_COUNT(5),
|
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE ),
|
|
||||||
HID_REPORT_SIZE(1),
|
|
||||||
HID_REPORT_COUNT(3),
|
HID_REPORT_COUNT(3),
|
||||||
HID_INPUT(HID_IOF_CONSTANT),
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
|
||||||
HID_END_COLLECTION,
|
HID_END_COLLECTION,
|
||||||
HID_END_COLLECTION,
|
HID_END_COLLECTION,
|
||||||
};
|
};
|
||||||
@ -122,25 +157,102 @@ static const struct HidConfigDescriptor hid_cfg_desc = {
|
|||||||
.bEndpointAddress = HID_RIN_EP,
|
.bEndpointAddress = HID_RIN_EP,
|
||||||
.bmAttributes = USB_EPTYPE_INTERRUPT,
|
.bmAttributes = USB_EPTYPE_INTERRUPT,
|
||||||
.wMaxPacketSize = HID_RIN_SZ,
|
.wMaxPacketSize = HID_RIN_SZ,
|
||||||
.bInterval = 50,
|
.bInterval = 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct {
|
struct HidReportMouse {
|
||||||
int8_t x;
|
uint8_t report_id;
|
||||||
int8_t y;
|
uint8_t btn;
|
||||||
uint8_t buttons;
|
int8_t x;
|
||||||
} __attribute__((packed)) hid_report_data;
|
int8_t y;
|
||||||
|
int8_t wheel;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct HidReportKB {
|
||||||
|
uint8_t report_id;
|
||||||
|
uint8_t mods;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t btn[HID_KB_MAX_KEYS];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static struct HidReport {
|
||||||
|
struct HidReportKB keyboard;
|
||||||
|
struct HidReportMouse mouse;
|
||||||
|
} __attribute__((packed)) hid_report;
|
||||||
|
|
||||||
static void hid_init(usbd_device* dev, struct UsbInterface* intf);
|
static void hid_init(usbd_device* dev, struct UsbInterface* intf);
|
||||||
static void hid_deinit(usbd_device *dev);
|
static void hid_deinit(usbd_device *dev);
|
||||||
static void hid_on_wakeup(usbd_device *dev);
|
static void hid_on_wakeup(usbd_device *dev);
|
||||||
static void hid_on_suspend(usbd_device *dev);
|
static void hid_on_suspend(usbd_device *dev);
|
||||||
|
|
||||||
|
static bool hid_send_report(uint8_t report_id);
|
||||||
static usbd_respond hid_ep_config (usbd_device *dev, uint8_t cfg);
|
static usbd_respond hid_ep_config (usbd_device *dev, uint8_t cfg);
|
||||||
static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
|
static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
|
||||||
static usbd_device* usb_dev;
|
static usbd_device* usb_dev;
|
||||||
|
static osSemaphoreId_t hid_semaphore = NULL;
|
||||||
|
static bool hid_connected = false;
|
||||||
|
|
||||||
|
bool furi_hal_hid_is_connected() {
|
||||||
|
return hid_connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_kb_press(uint16_t button) {
|
||||||
|
for (uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
||||||
|
if (hid_report.keyboard.btn[key_nb] == 0) {
|
||||||
|
hid_report.keyboard.btn[key_nb] = button & 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hid_report.keyboard.mods |= (button >> 8);
|
||||||
|
return hid_send_report(ReportIdKeyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_kb_release(uint16_t button) {
|
||||||
|
for (uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
||||||
|
if (hid_report.keyboard.btn[key_nb] == (button & 0xFF)) {
|
||||||
|
hid_report.keyboard.btn[key_nb] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hid_report.keyboard.mods &= ~(button >> 8);
|
||||||
|
return hid_send_report(ReportIdKeyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_kb_release_all() {
|
||||||
|
for (uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
||||||
|
hid_report.keyboard.btn[key_nb] = 0;
|
||||||
|
}
|
||||||
|
hid_report.keyboard.mods = 0;
|
||||||
|
return hid_send_report(ReportIdKeyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_mouse_move(int8_t dx, int8_t dy) {
|
||||||
|
hid_report.mouse.x = dx;
|
||||||
|
hid_report.mouse.y = dy;
|
||||||
|
bool state = hid_send_report(ReportIdMouse);
|
||||||
|
hid_report.mouse.x = 0;
|
||||||
|
hid_report.mouse.y = 0;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_mouse_press(uint8_t button) {
|
||||||
|
hid_report.mouse.btn |= button;
|
||||||
|
return hid_send_report(ReportIdMouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_mouse_release(uint8_t button) {
|
||||||
|
hid_report.mouse.btn &= ~button;
|
||||||
|
return hid_send_report(ReportIdMouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_hal_hid_mouse_scroll(int8_t delta) {
|
||||||
|
hid_report.mouse.wheel = delta;
|
||||||
|
bool state = hid_send_report(ReportIdMouse);
|
||||||
|
hid_report.mouse.wheel = 0;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
struct UsbInterface usb_hid = {
|
struct UsbInterface usb_hid = {
|
||||||
.init = hid_init,
|
.init = hid_init,
|
||||||
@ -158,7 +270,11 @@ struct UsbInterface usb_hid = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void hid_init(usbd_device* dev, struct UsbInterface* intf) {
|
static void hid_init(usbd_device* dev, struct UsbInterface* intf) {
|
||||||
|
if (hid_semaphore == NULL)
|
||||||
|
hid_semaphore = osSemaphoreNew(1, 1, NULL);
|
||||||
usb_dev = dev;
|
usb_dev = dev;
|
||||||
|
hid_report.keyboard.report_id = ReportIdKeyboard;
|
||||||
|
hid_report.mouse.report_id = ReportIdMouse;
|
||||||
|
|
||||||
usbd_reg_config(dev, hid_ep_config);
|
usbd_reg_config(dev, hid_ep_config);
|
||||||
usbd_reg_control(dev, hid_control);
|
usbd_reg_control(dev, hid_control);
|
||||||
@ -172,41 +288,34 @@ static void hid_deinit(usbd_device *dev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void hid_on_wakeup(usbd_device *dev) {
|
static void hid_on_wakeup(usbd_device *dev) {
|
||||||
|
hid_connected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hid_on_suspend(usbd_device *dev) {
|
static void hid_on_suspend(usbd_device *dev) {
|
||||||
|
if (hid_connected == true) {
|
||||||
|
hid_connected = false;
|
||||||
|
osSemaphoreRelease(hid_semaphore);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HID mouse IN endpoint callback */
|
static bool hid_send_report(uint8_t report_id)
|
||||||
static void hid_mouse_move(usbd_device *dev, uint8_t event, uint8_t ep) {
|
{
|
||||||
static uint8_t t = 0;
|
if ((hid_semaphore == NULL) || (hid_connected == false))
|
||||||
if (t < 0x10) {
|
return false;
|
||||||
hid_report_data.x = 1;
|
|
||||||
hid_report_data.y = 0;
|
furi_check(osSemaphoreAcquire(hid_semaphore, osWaitForever) == osOK);
|
||||||
} else if (t < 0x20) {
|
if (hid_connected == true) {
|
||||||
hid_report_data.x = 1;
|
if (report_id == ReportIdKeyboard)
|
||||||
hid_report_data.y = 1;
|
usbd_ep_write(usb_dev, HID_RIN_EP, &hid_report.keyboard, sizeof(hid_report.keyboard));
|
||||||
} else if (t < 0x30) {
|
else
|
||||||
hid_report_data.x = 0;
|
usbd_ep_write(usb_dev, HID_RIN_EP, &hid_report.mouse, sizeof(hid_report.mouse));
|
||||||
hid_report_data.y = 1;
|
return true;
|
||||||
} else if (t < 0x40) {
|
|
||||||
hid_report_data.x = -1;
|
|
||||||
hid_report_data.y = 1;
|
|
||||||
} else if (t < 0x50) {
|
|
||||||
hid_report_data.x = -1;
|
|
||||||
hid_report_data.y = 0;
|
|
||||||
} else if (t < 0x60) {
|
|
||||||
hid_report_data.x = -1;
|
|
||||||
hid_report_data.y = -1;
|
|
||||||
} else if (t < 0x70) {
|
|
||||||
hid_report_data.x = 0;
|
|
||||||
hid_report_data.y = -1;
|
|
||||||
} else {
|
|
||||||
hid_report_data.x = 1;
|
|
||||||
hid_report_data.y = -1;
|
|
||||||
}
|
}
|
||||||
t = (t + 1) & 0x7F;
|
return false;
|
||||||
usbd_ep_write(dev, ep, &hid_report_data, sizeof(hid_report_data));
|
}
|
||||||
|
|
||||||
|
static void hid_ep_callback(usbd_device *dev, uint8_t event, uint8_t ep) {
|
||||||
|
osSemaphoreRelease(hid_semaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure endpoints */
|
/* Configure endpoints */
|
||||||
@ -220,7 +329,7 @@ static usbd_respond hid_ep_config (usbd_device *dev, uint8_t cfg) {
|
|||||||
case 1:
|
case 1:
|
||||||
/* configuring device */
|
/* configuring device */
|
||||||
usbd_ep_config(dev, HID_RIN_EP, USB_EPTYPE_INTERRUPT, HID_RIN_SZ);
|
usbd_ep_config(dev, HID_RIN_EP, USB_EPTYPE_INTERRUPT, HID_RIN_SZ);
|
||||||
usbd_reg_endpoint(dev, HID_RIN_EP, hid_mouse_move);
|
usbd_reg_endpoint(dev, HID_RIN_EP, hid_ep_callback);
|
||||||
usbd_ep_write(dev, HID_RIN_EP, 0, 0);
|
usbd_ep_write(dev, HID_RIN_EP, 0, 0);
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
default:
|
default:
|
||||||
@ -237,8 +346,8 @@ static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_ca
|
|||||||
case USB_HID_SETIDLE:
|
case USB_HID_SETIDLE:
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
case USB_HID_GETREPORT:
|
case USB_HID_GETREPORT:
|
||||||
dev->status.data_ptr = &hid_report_data;
|
dev->status.data_ptr = &hid_report;
|
||||||
dev->status.data_count = sizeof(hid_report_data);
|
dev->status.data_count = sizeof(hid_report);
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
default:
|
default:
|
||||||
return usbd_fail;
|
return usbd_fail;
|
||||||
|
@ -89,6 +89,10 @@ void furi_hal_usb_set_config(UsbMode new_mode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UsbMode furi_hal_usb_get_config() {
|
||||||
|
return usb_config.mode_cur;
|
||||||
|
}
|
||||||
|
|
||||||
void furi_hal_usb_disable() {
|
void furi_hal_usb_disable() {
|
||||||
if (usb_config.enabled) {
|
if (usb_config.enabled) {
|
||||||
susp_evt(&udev, 0, 0);
|
susp_evt(&udev, 0, 0);
|
||||||
|
309
firmware/targets/furi-hal-include/furi-hal-usb-hid.h
Normal file
309
firmware/targets/furi-hal-include/furi-hal-usb-hid.h
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** HID keyboard key codes */
|
||||||
|
enum HidKeyboardKeys {
|
||||||
|
KEY_NONE = 0x00,
|
||||||
|
KEY_ERROR_ROLLOVER = 0x01,
|
||||||
|
KEY_POST_FAIL = 0x02,
|
||||||
|
KEY_ERROR_UNDEFINED = 0x03,
|
||||||
|
KEY_A = 0x04,
|
||||||
|
KEY_B = 0x05,
|
||||||
|
KEY_C = 0x06,
|
||||||
|
KEY_D = 0x07,
|
||||||
|
KEY_E = 0x08,
|
||||||
|
KEY_F = 0x09,
|
||||||
|
KEY_G = 0x0A,
|
||||||
|
KEY_H = 0x0B,
|
||||||
|
KEY_I = 0x0C,
|
||||||
|
KEY_J = 0x0D,
|
||||||
|
KEY_K = 0x0E,
|
||||||
|
KEY_L = 0x0F,
|
||||||
|
KEY_M = 0x10,
|
||||||
|
KEY_N = 0x11,
|
||||||
|
KEY_O = 0x12,
|
||||||
|
KEY_P = 0x13,
|
||||||
|
KEY_Q = 0x14,
|
||||||
|
KEY_R = 0x15,
|
||||||
|
KEY_S = 0x16,
|
||||||
|
KEY_T = 0x17,
|
||||||
|
KEY_U = 0x18,
|
||||||
|
KEY_V = 0x19,
|
||||||
|
KEY_W = 0x1A,
|
||||||
|
KEY_X = 0x1B,
|
||||||
|
KEY_Y = 0x1C,
|
||||||
|
KEY_Z = 0x1D,
|
||||||
|
KEY_1 = 0x1E,
|
||||||
|
KEY_2 = 0x1F,
|
||||||
|
KEY_3 = 0x20,
|
||||||
|
KEY_4 = 0x21,
|
||||||
|
KEY_5 = 0x22,
|
||||||
|
KEY_6 = 0x23,
|
||||||
|
KEY_7 = 0x24,
|
||||||
|
KEY_8 = 0x25,
|
||||||
|
KEY_9 = 0x26,
|
||||||
|
KEY_0 = 0x27,
|
||||||
|
KEY_ENTER = 0x28,
|
||||||
|
KEY_ESC = 0x29,
|
||||||
|
KEY_BACKSPACE = 0x2A,
|
||||||
|
KEY_TAB = 0x2B,
|
||||||
|
KEY_SPACE = 0x2C,
|
||||||
|
KEY_MINUS = 0x2D,
|
||||||
|
KEY_EQUAL = 0x2E,
|
||||||
|
KEY_LEFT_BRACE = 0x2F,
|
||||||
|
KEY_RIGHT_BRACE = 0x30,
|
||||||
|
KEY_BACKSLASH = 0x31,
|
||||||
|
KEY_NON_US_NUM = 0x32,
|
||||||
|
KEY_SEMICOLON = 0x33,
|
||||||
|
KEY_QUOTE = 0x34,
|
||||||
|
KEY_TILDE = 0x35,
|
||||||
|
KEY_COMMA = 0x36,
|
||||||
|
KEY_PERIOD = 0x37,
|
||||||
|
KEY_SLASH = 0x38,
|
||||||
|
KEY_CAPS_LOCK = 0x39,
|
||||||
|
KEY_F1 = 0x3A,
|
||||||
|
KEY_F2 = 0x3B,
|
||||||
|
KEY_F3 = 0x3C,
|
||||||
|
KEY_F4 = 0x3D,
|
||||||
|
KEY_F5 = 0x3E,
|
||||||
|
KEY_F6 = 0x3F,
|
||||||
|
KEY_F7 = 0x40,
|
||||||
|
KEY_F8 = 0x41,
|
||||||
|
KEY_F9 = 0x42,
|
||||||
|
KEY_F10 = 0x43,
|
||||||
|
KEY_F11 = 0x44,
|
||||||
|
KEY_F12 = 0x45,
|
||||||
|
KEY_PRINT = 0x46,
|
||||||
|
KEY_SCROLL_LOCK = 0x47,
|
||||||
|
KEY_PAUSE = 0x48,
|
||||||
|
KEY_INSERT = 0x49,
|
||||||
|
KEY_HOME = 0x4A,
|
||||||
|
KEY_PAGE_UP = 0x4B,
|
||||||
|
KEY_DELETE = 0x4C,
|
||||||
|
KEY_END = 0x4D,
|
||||||
|
KEY_PAGE_DOWN = 0x4E,
|
||||||
|
KEY_RIGHT_ARROW = 0x4F,
|
||||||
|
KEY_LEFT_ARROW = 0x50,
|
||||||
|
KEY_DOWN_ARROW = 0x51,
|
||||||
|
KEY_UP_ARROW = 0x52,
|
||||||
|
KEY_NUM_LOCK = 0x53,
|
||||||
|
KEYPAD_DIVIDE = 0x54,
|
||||||
|
KEYPAD_MULTIPLY = 0x55,
|
||||||
|
KEYPAD_SUBTRACT = 0x56,
|
||||||
|
KEYPAD_ADD = 0x57,
|
||||||
|
KEYPAD_ENTER = 0x58,
|
||||||
|
KEYPAD_1 = 0x59,
|
||||||
|
KEYPAD_2 = 0x5A,
|
||||||
|
KEYPAD_3 = 0x5B,
|
||||||
|
KEYPAD_4 = 0x5C,
|
||||||
|
KEYPAD_5 = 0x5D,
|
||||||
|
KEYPAD_6 = 0x5E,
|
||||||
|
KEYPAD_7 = 0x5F,
|
||||||
|
KEYPAD_8 = 0x60,
|
||||||
|
KEYPAD_9 = 0x61,
|
||||||
|
KEYPAD_0 = 0x62,
|
||||||
|
KEYPAD_DOT = 0x63,
|
||||||
|
KEY_NON_US = 0x64,
|
||||||
|
KEY_APPLICATION = 0x65,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** HID keyboard modifier keys */
|
||||||
|
enum HidKeyboardMods {
|
||||||
|
KEY_MOD_LEFT_CTRL = (1 << 8),
|
||||||
|
KEY_MOD_LEFT_SHIFT = (1 << 9),
|
||||||
|
KEY_MOD_LEFT_ALT = (1 << 10),
|
||||||
|
KEY_MOD_LEFT_GUI = (1 << 11),
|
||||||
|
KEY_MOD_RIGHT_CTRL = (1 << 12),
|
||||||
|
KEY_MOD_RIGHT_SHIFT = (1 << 13),
|
||||||
|
KEY_MOD_RIGHT_ALT = (1 << 14),
|
||||||
|
KEY_MOD_RIGHT_GUI = (1 << 15),
|
||||||
|
};
|
||||||
|
|
||||||
|
/** ASCII to keycode conversion table */
|
||||||
|
static const uint16_t hid_asciimap[] = {
|
||||||
|
KEY_NONE, // NUL
|
||||||
|
KEY_NONE, // SOH
|
||||||
|
KEY_NONE, // STX
|
||||||
|
KEY_NONE, // ETX
|
||||||
|
KEY_NONE, // EOT
|
||||||
|
KEY_NONE, // ENQ
|
||||||
|
KEY_NONE, // ACK
|
||||||
|
KEY_NONE, // BEL
|
||||||
|
KEY_BACKSPACE, // BS Backspace
|
||||||
|
KEY_TAB, // TAB Tab
|
||||||
|
KEY_ENTER, // LF Enter
|
||||||
|
KEY_NONE, // VT
|
||||||
|
KEY_NONE, // FF
|
||||||
|
KEY_NONE, // CR
|
||||||
|
KEY_NONE, // SO
|
||||||
|
KEY_NONE, // SI
|
||||||
|
KEY_NONE, // DEL
|
||||||
|
KEY_NONE, // DC1
|
||||||
|
KEY_NONE, // DC2
|
||||||
|
KEY_NONE, // DC3
|
||||||
|
KEY_NONE, // DC4
|
||||||
|
KEY_NONE, // NAK
|
||||||
|
KEY_NONE, // SYN
|
||||||
|
KEY_NONE, // ETB
|
||||||
|
KEY_NONE, // CAN
|
||||||
|
KEY_NONE, // EM
|
||||||
|
KEY_NONE, // SUB
|
||||||
|
KEY_NONE, // ESC
|
||||||
|
KEY_NONE, // FS
|
||||||
|
KEY_NONE, // GS
|
||||||
|
KEY_NONE, // RS
|
||||||
|
KEY_NONE, // US
|
||||||
|
KEY_SPACE, // ' ' Space
|
||||||
|
KEY_1 | KEY_MOD_LEFT_SHIFT, // !
|
||||||
|
KEY_QUOTE | KEY_MOD_LEFT_SHIFT, // "
|
||||||
|
KEY_3 | KEY_MOD_LEFT_SHIFT, // #
|
||||||
|
KEY_4 | KEY_MOD_LEFT_SHIFT, // $
|
||||||
|
KEY_5 | KEY_MOD_LEFT_SHIFT, // %
|
||||||
|
KEY_7 | KEY_MOD_LEFT_SHIFT, // &
|
||||||
|
KEY_QUOTE, // '
|
||||||
|
KEY_9 | KEY_MOD_LEFT_SHIFT, // (
|
||||||
|
KEY_0 | KEY_MOD_LEFT_SHIFT, // )
|
||||||
|
KEY_8 | KEY_MOD_LEFT_SHIFT, // *
|
||||||
|
KEY_EQUAL | KEY_MOD_LEFT_SHIFT, // +
|
||||||
|
KEY_COMMA, // ,
|
||||||
|
KEY_MINUS, // -
|
||||||
|
KEY_PERIOD, // .
|
||||||
|
KEY_SLASH, // /
|
||||||
|
KEY_0, // 0
|
||||||
|
KEY_1, // 1
|
||||||
|
KEY_2, // 2
|
||||||
|
KEY_3, // 3
|
||||||
|
KEY_4, // 4
|
||||||
|
KEY_5, // 5
|
||||||
|
KEY_6, // 6
|
||||||
|
KEY_7, // 7
|
||||||
|
KEY_8, // 8
|
||||||
|
KEY_9, // 9
|
||||||
|
KEY_SEMICOLON | KEY_MOD_LEFT_SHIFT, // :
|
||||||
|
KEY_SEMICOLON, // ;
|
||||||
|
KEY_COMMA | KEY_MOD_LEFT_SHIFT, // <
|
||||||
|
KEY_EQUAL, // =
|
||||||
|
KEY_PERIOD | KEY_MOD_LEFT_SHIFT, // >
|
||||||
|
KEY_SLASH | KEY_MOD_LEFT_SHIFT, // ?
|
||||||
|
KEY_2 | KEY_MOD_LEFT_SHIFT, // @
|
||||||
|
KEY_A | KEY_MOD_LEFT_SHIFT, // A
|
||||||
|
KEY_B | KEY_MOD_LEFT_SHIFT, // B
|
||||||
|
KEY_C | KEY_MOD_LEFT_SHIFT, // C
|
||||||
|
KEY_D | KEY_MOD_LEFT_SHIFT, // D
|
||||||
|
KEY_E | KEY_MOD_LEFT_SHIFT, // E
|
||||||
|
KEY_F | KEY_MOD_LEFT_SHIFT, // F
|
||||||
|
KEY_G | KEY_MOD_LEFT_SHIFT, // G
|
||||||
|
KEY_H | KEY_MOD_LEFT_SHIFT, // H
|
||||||
|
KEY_I | KEY_MOD_LEFT_SHIFT, // I
|
||||||
|
KEY_J | KEY_MOD_LEFT_SHIFT, // J
|
||||||
|
KEY_K | KEY_MOD_LEFT_SHIFT, // K
|
||||||
|
KEY_L | KEY_MOD_LEFT_SHIFT, // L
|
||||||
|
KEY_M | KEY_MOD_LEFT_SHIFT, // M
|
||||||
|
KEY_N | KEY_MOD_LEFT_SHIFT, // N
|
||||||
|
KEY_O | KEY_MOD_LEFT_SHIFT, // O
|
||||||
|
KEY_P | KEY_MOD_LEFT_SHIFT, // P
|
||||||
|
KEY_Q | KEY_MOD_LEFT_SHIFT, // Q
|
||||||
|
KEY_R | KEY_MOD_LEFT_SHIFT, // R
|
||||||
|
KEY_S | KEY_MOD_LEFT_SHIFT, // S
|
||||||
|
KEY_T | KEY_MOD_LEFT_SHIFT, // T
|
||||||
|
KEY_U | KEY_MOD_LEFT_SHIFT, // U
|
||||||
|
KEY_V | KEY_MOD_LEFT_SHIFT, // V
|
||||||
|
KEY_W | KEY_MOD_LEFT_SHIFT, // W
|
||||||
|
KEY_X | KEY_MOD_LEFT_SHIFT, // X
|
||||||
|
KEY_Y | KEY_MOD_LEFT_SHIFT, // Y
|
||||||
|
KEY_Z | KEY_MOD_LEFT_SHIFT, // Z
|
||||||
|
KEY_LEFT_BRACE, // [
|
||||||
|
KEY_BACKSLASH, // bslash
|
||||||
|
KEY_RIGHT_BRACE, // ]
|
||||||
|
KEY_6 | KEY_MOD_LEFT_SHIFT, // ^
|
||||||
|
KEY_MINUS | KEY_MOD_LEFT_SHIFT, // _
|
||||||
|
KEY_TILDE, // `
|
||||||
|
KEY_A, // a
|
||||||
|
KEY_B, // b
|
||||||
|
KEY_C, // c
|
||||||
|
KEY_D, // d
|
||||||
|
KEY_E, // e
|
||||||
|
KEY_F, // f
|
||||||
|
KEY_G, // g
|
||||||
|
KEY_H, // h
|
||||||
|
KEY_I, // i
|
||||||
|
KEY_J, // j
|
||||||
|
KEY_K, // k
|
||||||
|
KEY_L, // l
|
||||||
|
KEY_M, // m
|
||||||
|
KEY_N, // n
|
||||||
|
KEY_O, // o
|
||||||
|
KEY_P, // p
|
||||||
|
KEY_Q, // q
|
||||||
|
KEY_R, // r
|
||||||
|
KEY_S, // s
|
||||||
|
KEY_T, // t
|
||||||
|
KEY_U, // u
|
||||||
|
KEY_V, // v
|
||||||
|
KEY_W, // w
|
||||||
|
KEY_X, // x
|
||||||
|
KEY_Y, // y
|
||||||
|
KEY_Z, // z
|
||||||
|
KEY_LEFT_BRACE | KEY_MOD_LEFT_SHIFT, // {
|
||||||
|
KEY_BACKSLASH | KEY_MOD_LEFT_SHIFT, // |
|
||||||
|
KEY_RIGHT_BRACE | KEY_MOD_LEFT_SHIFT, // }
|
||||||
|
KEY_TILDE | KEY_MOD_LEFT_SHIFT, // ~
|
||||||
|
KEY_NONE, // DEL
|
||||||
|
};
|
||||||
|
|
||||||
|
/** ASCII to keycode conversion macro */
|
||||||
|
#define HID_ASCII_TO_KEY(x) (((uint8_t)x < 128) ? (hid_asciimap[(uint8_t)x]) : KEY_NONE)
|
||||||
|
|
||||||
|
/** HID mouse buttons */
|
||||||
|
enum HidMouseButtons {
|
||||||
|
HID_MOUSE_BTN_LEFT = (1 << 0),
|
||||||
|
HID_MOUSE_BTN_RIGHT = (1 << 1),
|
||||||
|
HID_MOUSE_BTN_WHEEL = (1 << 2),
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Get USB HID connection state
|
||||||
|
*
|
||||||
|
* @return true / false
|
||||||
|
*/
|
||||||
|
bool furi_hal_hid_is_connected();
|
||||||
|
|
||||||
|
/** Set the following key to pressed state and send HID report
|
||||||
|
*
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool furi_hal_hid_kb_press(uint16_t button);
|
||||||
|
|
||||||
|
/** Set the following key to released state and send HID report
|
||||||
|
*
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool furi_hal_hid_kb_release(uint16_t button);
|
||||||
|
|
||||||
|
/** Clear all pressed keys and send HID report
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool furi_hal_hid_kb_release_all();
|
||||||
|
|
||||||
|
/** Set mouse movement and send HID report
|
||||||
|
*
|
||||||
|
* @param dx x coordinate delta
|
||||||
|
* @param dy y coordinate delta
|
||||||
|
*/
|
||||||
|
bool furi_hal_hid_mouse_move(int8_t dx, int8_t dy);
|
||||||
|
|
||||||
|
/** Set mouse button to pressed state and send HID report
|
||||||
|
*
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool furi_hal_hid_mouse_press(uint8_t button);
|
||||||
|
|
||||||
|
/** Set mouse button to released state and send HID report
|
||||||
|
*
|
||||||
|
* @param button key code
|
||||||
|
*/
|
||||||
|
bool furi_hal_hid_mouse_release(uint8_t button);
|
||||||
|
|
||||||
|
/** Set mouse wheel position and send HID report
|
||||||
|
*
|
||||||
|
* @param delta number of scroll steps
|
||||||
|
*/
|
||||||
|
bool furi_hal_hid_mouse_scroll(int8_t delta);
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
|
||||||
|
/** USB device modes */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UsbModeNone,
|
UsbModeNone,
|
||||||
UsbModeVcpSingle,
|
UsbModeVcpSingle,
|
||||||
@ -12,10 +13,26 @@ typedef enum {
|
|||||||
UsbModesNum,
|
UsbModesNum,
|
||||||
} UsbMode;
|
} UsbMode;
|
||||||
|
|
||||||
|
/** USB device low-level initialization
|
||||||
|
*/
|
||||||
void furi_hal_usb_init();
|
void furi_hal_usb_init();
|
||||||
|
|
||||||
|
/** Set USB device configuration
|
||||||
|
*
|
||||||
|
* @param mode new USB device mode
|
||||||
|
*/
|
||||||
void furi_hal_usb_set_config(UsbMode mode);
|
void furi_hal_usb_set_config(UsbMode mode);
|
||||||
|
|
||||||
|
/** Get USB device configuration
|
||||||
|
*
|
||||||
|
* @return current USB device mode
|
||||||
|
*/
|
||||||
|
UsbMode furi_hal_usb_get_config();
|
||||||
|
|
||||||
|
/** Disable USB device
|
||||||
|
*/
|
||||||
void furi_hal_usb_disable();
|
void furi_hal_usb_disable();
|
||||||
|
|
||||||
|
/** Enable USB device
|
||||||
|
*/
|
||||||
void furi_hal_usb_enable();
|
void furi_hal_usb_enable();
|
||||||
|
@ -34,6 +34,7 @@ template <unsigned int N> struct STOP_EXTERNING_ME {};
|
|||||||
#include "furi-hal-rfid.h"
|
#include "furi-hal-rfid.h"
|
||||||
#include "furi-hal-nfc.h"
|
#include "furi-hal-nfc.h"
|
||||||
#include "furi-hal-usb.h"
|
#include "furi-hal-usb.h"
|
||||||
|
#include "furi-hal-usb-hid.h"
|
||||||
|
|
||||||
/** Init furi-hal */
|
/** Init furi-hal */
|
||||||
void furi_hal_init();
|
void furi_hal_init();
|
||||||
|
Loading…
Reference in New Issue
Block a user