[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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Nikolay Minaylov
						Nikolay Minaylov