[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:
		@@ -35,6 +35,8 @@ extern int32_t subghz_app(void* p);
 | 
			
		||||
extern int32_t vibro_test_app(void* p);
 | 
			
		||||
extern int32_t bt_debug_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
 | 
			
		||||
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},
 | 
			
		||||
#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
 | 
			
		||||
    {.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = &A_Plugins_14},
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,8 @@ APP_KEYPAD_TEST = 1
 | 
			
		||||
APP_SD_TEST	= 1
 | 
			
		||||
APP_VIBRO_DEMO = 1
 | 
			
		||||
APP_USB_TEST = 1
 | 
			
		||||
APP_USB_MOUSE = 1
 | 
			
		||||
APP_BAD_USB = 1
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -127,8 +129,21 @@ ifeq ($(APP_USB_TEST), 1)
 | 
			
		||||
CFLAGS		+= -DAPP_USB_TEST
 | 
			
		||||
SRV_INPUT = 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
 | 
			
		||||
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/modules/submenu.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <cmsis_os.h>
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    Gui* gui;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,13 @@
 | 
			
		||||
#include "usb_hid.h"
 | 
			
		||||
#include "hid_usage_desktop.h"
 | 
			
		||||
#include "hid_usage_button.h"
 | 
			
		||||
#include "hid_usage_keyboard.h"
 | 
			
		||||
 | 
			
		||||
#define HID_RIN_EP      0x81
 | 
			
		||||
#define HID_RIN_SZ      0x10
 | 
			
		||||
 | 
			
		||||
#define HID_KB_MAX_KEYS 6
 | 
			
		||||
 | 
			
		||||
struct HidIadDescriptor {
 | 
			
		||||
    struct usb_iad_descriptor           hid_iad;
 | 
			
		||||
    struct usb_interface_descriptor     hid;
 | 
			
		||||
@@ -22,31 +25,63 @@ struct HidConfigDescriptor {
 | 
			
		||||
    struct HidIadDescriptor             iad_0;
 | 
			
		||||
} __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[] = {
 | 
			
		||||
    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(HID_DESKTOP_MOUSE),
 | 
			
		||||
    HID_COLLECTION(HID_APPLICATION_COLLECTION),
 | 
			
		||||
        HID_USAGE(HID_DESKTOP_POINTER),
 | 
			
		||||
        HID_COLLECTION(HID_PHYSICAL_COLLECTION),
 | 
			
		||||
            HID_REPORT_ID(ReportIdMouse),
 | 
			
		||||
            HID_USAGE_PAGE(HID_PAGE_BUTTON),
 | 
			
		||||
            HID_USAGE_MINIMUM(1),
 | 
			
		||||
            HID_USAGE_MAXIMUM(3),
 | 
			
		||||
            HID_LOGICAL_MINIMUM(0),
 | 
			
		||||
            HID_LOGICAL_MAXIMUM(1),
 | 
			
		||||
            HID_REPORT_COUNT(3),
 | 
			
		||||
            HID_REPORT_SIZE(1),
 | 
			
		||||
            HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
 | 
			
		||||
            HID_REPORT_SIZE(1),
 | 
			
		||||
            HID_REPORT_COUNT(5),
 | 
			
		||||
            HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
 | 
			
		||||
            HID_USAGE_PAGE(HID_PAGE_DESKTOP),
 | 
			
		||||
            HID_USAGE(HID_DESKTOP_X),
 | 
			
		||||
            HID_USAGE(HID_DESKTOP_Y),
 | 
			
		||||
            HID_USAGE(HID_DESKTOP_WHEEL),
 | 
			
		||||
            HID_LOGICAL_MINIMUM(-127),
 | 
			
		||||
            HID_LOGICAL_MAXIMUM(127),
 | 
			
		||||
            HID_REPORT_SIZE(8),
 | 
			
		||||
            HID_REPORT_COUNT(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_INPUT(HID_IOF_CONSTANT),
 | 
			
		||||
            HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
 | 
			
		||||
        HID_END_COLLECTION,
 | 
			
		||||
    HID_END_COLLECTION,
 | 
			
		||||
};
 | 
			
		||||
@@ -122,25 +157,102 @@ static const struct HidConfigDescriptor hid_cfg_desc = {
 | 
			
		||||
            .bEndpointAddress       = HID_RIN_EP,
 | 
			
		||||
            .bmAttributes           = USB_EPTYPE_INTERRUPT,
 | 
			
		||||
            .wMaxPacketSize         = HID_RIN_SZ,
 | 
			
		||||
            .bInterval              = 50,
 | 
			
		||||
            .bInterval              = 10,
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
    int8_t      x;
 | 
			
		||||
    int8_t      y;
 | 
			
		||||
    uint8_t     buttons;
 | 
			
		||||
} __attribute__((packed)) hid_report_data;
 | 
			
		||||
struct HidReportMouse {
 | 
			
		||||
    uint8_t report_id;
 | 
			
		||||
    uint8_t btn;
 | 
			
		||||
    int8_t x;
 | 
			
		||||
    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_deinit(usbd_device *dev);
 | 
			
		||||
static void hid_on_wakeup(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_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
 | 
			
		||||
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 = {
 | 
			
		||||
    .init = hid_init,
 | 
			
		||||
@@ -158,7 +270,11 @@ struct UsbInterface usb_hid = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void hid_init(usbd_device* dev, struct UsbInterface* intf) {
 | 
			
		||||
    if (hid_semaphore == NULL)
 | 
			
		||||
        hid_semaphore = osSemaphoreNew(1, 1, NULL);
 | 
			
		||||
    usb_dev = dev;
 | 
			
		||||
    hid_report.keyboard.report_id = ReportIdKeyboard;
 | 
			
		||||
    hid_report.mouse.report_id = ReportIdMouse;
 | 
			
		||||
 | 
			
		||||
    usbd_reg_config(dev, hid_ep_config);
 | 
			
		||||
    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) {
 | 
			
		||||
    hid_connected = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hid_on_suspend(usbd_device *dev) {
 | 
			
		||||
    if (hid_connected == true) {
 | 
			
		||||
        hid_connected = false;
 | 
			
		||||
        osSemaphoreRelease(hid_semaphore);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* HID mouse IN endpoint callback */
 | 
			
		||||
static void hid_mouse_move(usbd_device *dev, uint8_t event, uint8_t ep) {
 | 
			
		||||
    static uint8_t t = 0;
 | 
			
		||||
    if (t < 0x10) {
 | 
			
		||||
        hid_report_data.x = 1;
 | 
			
		||||
        hid_report_data.y = 0;
 | 
			
		||||
    } else if (t < 0x20) {
 | 
			
		||||
        hid_report_data.x = 1;
 | 
			
		||||
        hid_report_data.y = 1;
 | 
			
		||||
    } else if (t < 0x30) {
 | 
			
		||||
        hid_report_data.x = 0;
 | 
			
		||||
        hid_report_data.y = 1;
 | 
			
		||||
    } 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;
 | 
			
		||||
static bool hid_send_report(uint8_t report_id)
 | 
			
		||||
{
 | 
			
		||||
    if ((hid_semaphore == NULL) || (hid_connected == false))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    furi_check(osSemaphoreAcquire(hid_semaphore, osWaitForever) == osOK);
 | 
			
		||||
    if (hid_connected == true) {
 | 
			
		||||
        if (report_id == ReportIdKeyboard)
 | 
			
		||||
            usbd_ep_write(usb_dev, HID_RIN_EP, &hid_report.keyboard, sizeof(hid_report.keyboard));
 | 
			
		||||
        else
 | 
			
		||||
            usbd_ep_write(usb_dev, HID_RIN_EP, &hid_report.mouse, sizeof(hid_report.mouse));
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    t = (t + 1) & 0x7F;
 | 
			
		||||
    usbd_ep_write(dev, ep, &hid_report_data, sizeof(hid_report_data));
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hid_ep_callback(usbd_device *dev, uint8_t event, uint8_t ep) {
 | 
			
		||||
    osSemaphoreRelease(hid_semaphore);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Configure endpoints */
 | 
			
		||||
@@ -220,7 +329,7 @@ static usbd_respond hid_ep_config (usbd_device *dev, uint8_t cfg) {
 | 
			
		||||
    case 1:
 | 
			
		||||
        /* configuring device */
 | 
			
		||||
        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);
 | 
			
		||||
        return usbd_ack;
 | 
			
		||||
    default:
 | 
			
		||||
@@ -237,8 +346,8 @@ static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_ca
 | 
			
		||||
        case USB_HID_SETIDLE:
 | 
			
		||||
            return usbd_ack;
 | 
			
		||||
        case USB_HID_GETREPORT:
 | 
			
		||||
            dev->status.data_ptr = &hid_report_data;
 | 
			
		||||
            dev->status.data_count = sizeof(hid_report_data);
 | 
			
		||||
            dev->status.data_ptr = &hid_report;
 | 
			
		||||
            dev->status.data_count = sizeof(hid_report);
 | 
			
		||||
            return usbd_ack;
 | 
			
		||||
        default:
 | 
			
		||||
            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() {
 | 
			
		||||
    if (usb_config.enabled) {
 | 
			
		||||
        susp_evt(&udev, 0, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,13 @@
 | 
			
		||||
#include "usb_hid.h"
 | 
			
		||||
#include "hid_usage_desktop.h"
 | 
			
		||||
#include "hid_usage_button.h"
 | 
			
		||||
#include "hid_usage_keyboard.h"
 | 
			
		||||
 | 
			
		||||
#define HID_RIN_EP      0x81
 | 
			
		||||
#define HID_RIN_SZ      0x10
 | 
			
		||||
 | 
			
		||||
#define HID_KB_MAX_KEYS 6
 | 
			
		||||
 | 
			
		||||
struct HidIadDescriptor {
 | 
			
		||||
    struct usb_iad_descriptor           hid_iad;
 | 
			
		||||
    struct usb_interface_descriptor     hid;
 | 
			
		||||
@@ -22,31 +25,63 @@ struct HidConfigDescriptor {
 | 
			
		||||
    struct HidIadDescriptor             iad_0;
 | 
			
		||||
} __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[] = {
 | 
			
		||||
    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(HID_DESKTOP_MOUSE),
 | 
			
		||||
    HID_COLLECTION(HID_APPLICATION_COLLECTION),
 | 
			
		||||
        HID_USAGE(HID_DESKTOP_POINTER),
 | 
			
		||||
        HID_COLLECTION(HID_PHYSICAL_COLLECTION),
 | 
			
		||||
            HID_REPORT_ID(ReportIdMouse),
 | 
			
		||||
            HID_USAGE_PAGE(HID_PAGE_BUTTON),
 | 
			
		||||
            HID_USAGE_MINIMUM(1),
 | 
			
		||||
            HID_USAGE_MAXIMUM(3),
 | 
			
		||||
            HID_LOGICAL_MINIMUM(0),
 | 
			
		||||
            HID_LOGICAL_MAXIMUM(1),
 | 
			
		||||
            HID_REPORT_COUNT(3),
 | 
			
		||||
            HID_REPORT_SIZE(1),
 | 
			
		||||
            HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
 | 
			
		||||
            HID_REPORT_SIZE(1),
 | 
			
		||||
            HID_REPORT_COUNT(5),
 | 
			
		||||
            HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
 | 
			
		||||
            HID_USAGE_PAGE(HID_PAGE_DESKTOP),
 | 
			
		||||
            HID_USAGE(HID_DESKTOP_X),
 | 
			
		||||
            HID_USAGE(HID_DESKTOP_Y),
 | 
			
		||||
            HID_USAGE(HID_DESKTOP_WHEEL),
 | 
			
		||||
            HID_LOGICAL_MINIMUM(-127),
 | 
			
		||||
            HID_LOGICAL_MAXIMUM(127),
 | 
			
		||||
            HID_REPORT_SIZE(8),
 | 
			
		||||
            HID_REPORT_COUNT(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_INPUT(HID_IOF_CONSTANT),
 | 
			
		||||
            HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
 | 
			
		||||
        HID_END_COLLECTION,
 | 
			
		||||
    HID_END_COLLECTION,
 | 
			
		||||
};
 | 
			
		||||
@@ -122,25 +157,102 @@ static const struct HidConfigDescriptor hid_cfg_desc = {
 | 
			
		||||
            .bEndpointAddress       = HID_RIN_EP,
 | 
			
		||||
            .bmAttributes           = USB_EPTYPE_INTERRUPT,
 | 
			
		||||
            .wMaxPacketSize         = HID_RIN_SZ,
 | 
			
		||||
            .bInterval              = 50,
 | 
			
		||||
            .bInterval              = 10,
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
    int8_t      x;
 | 
			
		||||
    int8_t      y;
 | 
			
		||||
    uint8_t     buttons;
 | 
			
		||||
} __attribute__((packed)) hid_report_data;
 | 
			
		||||
struct HidReportMouse {
 | 
			
		||||
    uint8_t report_id;
 | 
			
		||||
    uint8_t btn;
 | 
			
		||||
    int8_t x;
 | 
			
		||||
    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_deinit(usbd_device *dev);
 | 
			
		||||
static void hid_on_wakeup(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_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
 | 
			
		||||
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 = {
 | 
			
		||||
    .init = hid_init,
 | 
			
		||||
@@ -158,7 +270,11 @@ struct UsbInterface usb_hid = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void hid_init(usbd_device* dev, struct UsbInterface* intf) {
 | 
			
		||||
    if (hid_semaphore == NULL)
 | 
			
		||||
        hid_semaphore = osSemaphoreNew(1, 1, NULL);
 | 
			
		||||
    usb_dev = dev;
 | 
			
		||||
    hid_report.keyboard.report_id = ReportIdKeyboard;
 | 
			
		||||
    hid_report.mouse.report_id = ReportIdMouse;
 | 
			
		||||
 | 
			
		||||
    usbd_reg_config(dev, hid_ep_config);
 | 
			
		||||
    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) {
 | 
			
		||||
    hid_connected = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hid_on_suspend(usbd_device *dev) {
 | 
			
		||||
    if (hid_connected == true) {
 | 
			
		||||
        hid_connected = false;
 | 
			
		||||
        osSemaphoreRelease(hid_semaphore);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* HID mouse IN endpoint callback */
 | 
			
		||||
static void hid_mouse_move(usbd_device *dev, uint8_t event, uint8_t ep) {
 | 
			
		||||
    static uint8_t t = 0;
 | 
			
		||||
    if (t < 0x10) {
 | 
			
		||||
        hid_report_data.x = 1;
 | 
			
		||||
        hid_report_data.y = 0;
 | 
			
		||||
    } else if (t < 0x20) {
 | 
			
		||||
        hid_report_data.x = 1;
 | 
			
		||||
        hid_report_data.y = 1;
 | 
			
		||||
    } else if (t < 0x30) {
 | 
			
		||||
        hid_report_data.x = 0;
 | 
			
		||||
        hid_report_data.y = 1;
 | 
			
		||||
    } 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;
 | 
			
		||||
static bool hid_send_report(uint8_t report_id)
 | 
			
		||||
{
 | 
			
		||||
    if ((hid_semaphore == NULL) || (hid_connected == false))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    furi_check(osSemaphoreAcquire(hid_semaphore, osWaitForever) == osOK);
 | 
			
		||||
    if (hid_connected == true) {
 | 
			
		||||
        if (report_id == ReportIdKeyboard)
 | 
			
		||||
            usbd_ep_write(usb_dev, HID_RIN_EP, &hid_report.keyboard, sizeof(hid_report.keyboard));
 | 
			
		||||
        else
 | 
			
		||||
            usbd_ep_write(usb_dev, HID_RIN_EP, &hid_report.mouse, sizeof(hid_report.mouse));
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    t = (t + 1) & 0x7F;
 | 
			
		||||
    usbd_ep_write(dev, ep, &hid_report_data, sizeof(hid_report_data));
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hid_ep_callback(usbd_device *dev, uint8_t event, uint8_t ep) {
 | 
			
		||||
    osSemaphoreRelease(hid_semaphore);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Configure endpoints */
 | 
			
		||||
@@ -220,7 +329,7 @@ static usbd_respond hid_ep_config (usbd_device *dev, uint8_t cfg) {
 | 
			
		||||
    case 1:
 | 
			
		||||
        /* configuring device */
 | 
			
		||||
        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);
 | 
			
		||||
        return usbd_ack;
 | 
			
		||||
    default:
 | 
			
		||||
@@ -237,8 +346,8 @@ static usbd_respond hid_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_ca
 | 
			
		||||
        case USB_HID_SETIDLE:
 | 
			
		||||
            return usbd_ack;
 | 
			
		||||
        case USB_HID_GETREPORT:
 | 
			
		||||
            dev->status.data_ptr = &hid_report_data;
 | 
			
		||||
            dev->status.data_count = sizeof(hid_report_data);
 | 
			
		||||
            dev->status.data_ptr = &hid_report;
 | 
			
		||||
            dev->status.data_count = sizeof(hid_report);
 | 
			
		||||
            return usbd_ack;
 | 
			
		||||
        default:
 | 
			
		||||
            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() {
 | 
			
		||||
    if (usb_config.enabled) {
 | 
			
		||||
        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"
 | 
			
		||||
 | 
			
		||||
/** USB device modes */
 | 
			
		||||
typedef enum {
 | 
			
		||||
    UsbModeNone,
 | 
			
		||||
    UsbModeVcpSingle,
 | 
			
		||||
@@ -12,10 +13,26 @@ typedef enum {
 | 
			
		||||
    UsbModesNum,
 | 
			
		||||
} UsbMode;
 | 
			
		||||
 | 
			
		||||
/** USB device low-level initialization
 | 
			
		||||
 */
 | 
			
		||||
void furi_hal_usb_init();
 | 
			
		||||
 | 
			
		||||
/** Set USB device configuration
 | 
			
		||||
 *
 | 
			
		||||
 * @param      mode new USB device 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();
 | 
			
		||||
 | 
			
		||||
/** Enable USB device
 | 
			
		||||
 */
 | 
			
		||||
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-nfc.h"
 | 
			
		||||
#include "furi-hal-usb.h"
 | 
			
		||||
#include "furi-hal-usb-hid.h"
 | 
			
		||||
 | 
			
		||||
/** Init furi-hal */
 | 
			
		||||
void furi_hal_init();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user