From 0ce1b8941a2735d4eec9df02e9595198dbf39162 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Sun, 24 Mar 2024 18:30:08 +0000 Subject: [PATCH] refactor(mouse): Lots of mouse refactors. * Dedicated mouse source directory. * Split mouse HID into dedicated USB endpoint and HoG service. * Enable composite USB device automatically, tweak the various default sizes. --- app/CMakeLists.txt | 1 + app/include/zmk/endpoints.h | 2 +- app/include/zmk/hid.h | 77 ------- app/include/zmk/hog.h | 4 - app/include/zmk/mouse/hid.h | 84 ++++++++ app/include/zmk/mouse/hog.h | 13 ++ app/include/zmk/{mouse.h => mouse/types.h} | 0 app/include/zmk/mouse/usb_hid.h | 13 ++ app/src/behaviors/behavior_mouse_key_press.c | 4 +- app/src/endpoints.c | 13 +- app/src/hid.c | 111 ---------- app/src/hog.c | 84 +------- app/src/main.c | 4 - app/src/mouse/CMakeLists.txt | 6 + app/src/mouse/Kconfig | 13 +- app/src/mouse/hid.c | 107 ++++++++++ app/src/mouse/hog.c | 200 +++++++++++++++++++ app/src/mouse/input_listener.c | 90 +++++---- app/src/mouse/usb_hid.c | 148 ++++++++++++++ app/src/usb_hid.c | 13 -- 20 files changed, 652 insertions(+), 335 deletions(-) create mode 100644 app/include/zmk/mouse/hid.h create mode 100644 app/include/zmk/mouse/hog.h rename app/include/zmk/{mouse.h => mouse/types.h} (100%) create mode 100644 app/include/zmk/mouse/usb_hid.h create mode 100644 app/src/mouse/CMakeLists.txt create mode 100644 app/src/mouse/hid.c create mode 100644 app/src/mouse/hog.c create mode 100644 app/src/mouse/usb_hid.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 67fe91014e03..88dc737aabd5 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -100,5 +100,6 @@ target_sources(app PRIVATE src/main.c) add_subdirectory(src/display/) add_subdirectory_ifdef(CONFIG_SETTINGS src/settings/) +add_subdirectory(src/mouse/) zephyr_cc_option(-Wfatal-errors) diff --git a/app/include/zmk/endpoints.h b/app/include/zmk/endpoints.h index f2aff2bcc2df..0177416d10d1 100644 --- a/app/include/zmk/endpoints.h +++ b/app/include/zmk/endpoints.h @@ -72,6 +72,6 @@ int zmk_endpoints_send_report(uint16_t usage_page); #if IS_ENABLED(CONFIG_ZMK_MOUSE) int zmk_endpoints_send_mouse_report(); -#endif // IS_ENABLE(CONFIG_ZMK_MOUSE) +#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) void zmk_endpoints_clear_current(void); diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index b4c29b7aa66a..22af6d6e9b11 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -10,9 +10,6 @@ #include #include -#if IS_ENABLED(CONFIG_ZMK_MOUSE) -#include -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #include #include @@ -23,8 +20,6 @@ #define ZMK_HID_KEYBOARD_NKRO_MAX_USAGE HID_USAGE_KEY_KEYPAD_EQUAL #endif -#define ZMK_HID_MOUSE_NUM_BUTTONS 0x05 - // See https://www.usb.org/sites/default/files/hid1_11.pdf section 6.2.2.4 Main Items #define ZMK_HID_MAIN_VAL_DATA (0x00 << 0) @@ -57,7 +52,6 @@ #define ZMK_HID_REPORT_ID_KEYBOARD 0x01 #define ZMK_HID_REPORT_ID_LEDS 0x01 #define ZMK_HID_REPORT_ID_CONSUMER 0x02 -#define ZMK_HID_REPORT_ID_MOUSE 0x03 // Needed until Zephyr offers a 2 byte usage macro #define HID_USAGE16(idx) \ @@ -147,45 +141,6 @@ static const uint8_t zmk_hid_report_desc[] = { HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_ARRAY | ZMK_HID_MAIN_VAL_ABS), HID_END_COLLECTION, -#if IS_ENABLED(CONFIG_ZMK_MOUSE) - HID_USAGE_PAGE(HID_USAGE_GD), - HID_USAGE(HID_USAGE_GD_MOUSE), - HID_COLLECTION(HID_COLLECTION_APPLICATION), - HID_REPORT_ID(ZMK_HID_REPORT_ID_MOUSE), - HID_USAGE(HID_USAGE_GD_POINTER), - HID_COLLECTION(HID_COLLECTION_PHYSICAL), - HID_USAGE_PAGE(HID_USAGE_BUTTON), - HID_USAGE_MIN8(0x1), - HID_USAGE_MAX8(ZMK_HID_MOUSE_NUM_BUTTONS), - HID_LOGICAL_MIN8(0x00), - HID_LOGICAL_MAX8(0x01), - HID_REPORT_SIZE(0x01), - HID_REPORT_COUNT(0x5), - HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), - // Constant padding for the last 3 bits. - HID_REPORT_SIZE(0x03), - HID_REPORT_COUNT(0x01), - HID_INPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), - // Some OSes ignore pointer devices without X/Y data. - HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP), - HID_USAGE(HID_USAGE_GD_X), - HID_USAGE(HID_USAGE_GD_Y), - HID_USAGE(HID_USAGE_GD_WHEEL), - HID_LOGICAL_MIN16(0xFF, -0x7F), - HID_LOGICAL_MAX16(0xFF, 0x7F), - HID_REPORT_SIZE(0x10), - HID_REPORT_COUNT(0x03), - HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL), - HID_USAGE_PAGE(HID_USAGE_CONSUMER), - HID_USAGE16(HID_USAGE_CONSUMER_AC_PAN), - HID_LOGICAL_MIN16(0xFF, -0x7F), - HID_LOGICAL_MAX16(0xFF, 0x7F), - HID_REPORT_SIZE(0x08), - HID_REPORT_COUNT(0x01), - HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL), - HID_END_COLLECTION, - HID_END_COLLECTION, -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) }; #if IS_ENABLED(CONFIG_ZMK_USB_BOOT) @@ -248,22 +203,6 @@ struct zmk_hid_consumer_report { struct zmk_hid_consumer_report_body body; } __packed; -#if IS_ENABLED(CONFIG_ZMK_MOUSE) -struct zmk_hid_mouse_report_body { - zmk_mouse_button_flags_t buttons; - int16_t d_x; - int16_t d_y; - int16_t d_scroll_y; - int16_t d_scroll_x; -} __packed; - -struct zmk_hid_mouse_report { - uint8_t report_id; - struct zmk_hid_mouse_report_body body; -} __packed; - -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - zmk_mod_flags_t zmk_hid_get_explicit_mods(void); int zmk_hid_register_mod(zmk_mod_t modifier); int zmk_hid_unregister_mod(zmk_mod_t modifier); @@ -290,25 +229,9 @@ int zmk_hid_press(uint32_t usage); int zmk_hid_release(uint32_t usage); bool zmk_hid_is_pressed(uint32_t usage); -#if IS_ENABLED(CONFIG_ZMK_MOUSE) -int zmk_hid_mouse_button_press(zmk_mouse_button_t button); -int zmk_hid_mouse_button_release(zmk_mouse_button_t button); -int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons); -int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons); -void zmk_hid_mouse_movement_set(int16_t x, int16_t y); -void zmk_hid_mouse_scroll_set(int8_t x, int8_t y); -void zmk_hid_mouse_movement_update(int16_t x, int16_t y); -void zmk_hid_mouse_scroll_update(int8_t x, int8_t y); -void zmk_hid_mouse_clear(void); -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void); struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void); #if IS_ENABLED(CONFIG_ZMK_USB_BOOT) zmk_hid_boot_report_t *zmk_hid_get_boot_report(); #endif - -#if IS_ENABLED(CONFIG_ZMK_MOUSE) -struct zmk_hid_mouse_report *zmk_hid_get_mouse_report(); -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/include/zmk/hog.h b/app/include/zmk/hog.h index eb6e653f772e..4e1603037a54 100644 --- a/app/include/zmk/hog.h +++ b/app/include/zmk/hog.h @@ -11,7 +11,3 @@ int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body); int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body); - -#if IS_ENABLED(CONFIG_ZMK_MOUSE) -int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body); -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/include/zmk/mouse/hid.h b/app/include/zmk/mouse/hid.h new file mode 100644 index 000000000000..ba615d88c76d --- /dev/null +++ b/app/include/zmk/mouse/hid.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +#include +#include + +#include + +#include +#include + +#define ZMK_MOUSE_HID_NUM_BUTTONS 0x05 + +#define ZMK_MOUSE_HID_REPORT_ID_MOUSE 0x01 + +static const uint8_t zmk_mouse_hid_report_desc[] = { + HID_USAGE_PAGE(HID_USAGE_GD), + HID_USAGE(HID_USAGE_GD_MOUSE), + HID_COLLECTION(HID_COLLECTION_APPLICATION), + HID_REPORT_ID(ZMK_MOUSE_HID_REPORT_ID_MOUSE), + HID_USAGE(HID_USAGE_GD_POINTER), + HID_COLLECTION(HID_COLLECTION_PHYSICAL), + HID_USAGE_PAGE(HID_USAGE_BUTTON), + HID_USAGE_MIN8(0x1), + HID_USAGE_MAX8(ZMK_MOUSE_HID_NUM_BUTTONS), + HID_LOGICAL_MIN8(0x00), + HID_LOGICAL_MAX8(0x01), + HID_REPORT_SIZE(0x01), + HID_REPORT_COUNT(0x5), + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + // Constant padding for the last 3 bits. + HID_REPORT_SIZE(0x03), + HID_REPORT_COUNT(0x01), + HID_INPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + // Some OSes ignore pointer devices without X/Y data. + HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP), + HID_USAGE(HID_USAGE_GD_X), + HID_USAGE(HID_USAGE_GD_Y), + HID_USAGE(HID_USAGE_GD_WHEEL), + HID_LOGICAL_MIN16(0xFF, -0x7F), + HID_LOGICAL_MAX16(0xFF, 0x7F), + HID_REPORT_SIZE(0x10), + HID_REPORT_COUNT(0x03), + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL), + HID_USAGE_PAGE(HID_USAGE_CONSUMER), + HID_USAGE16(HID_USAGE_CONSUMER_AC_PAN), + HID_REPORT_COUNT(0x01), + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL), + HID_END_COLLECTION, + HID_END_COLLECTION, +}; + +struct zmk_hid_mouse_report_body { + zmk_mouse_button_flags_t buttons; + int16_t d_x; + int16_t d_y; + int16_t d_scroll_y; + int16_t d_scroll_x; +} __packed; + +struct zmk_hid_mouse_report { + uint8_t report_id; + struct zmk_hid_mouse_report_body body; +} __packed; + +int zmk_hid_mouse_button_press(zmk_mouse_button_t button); +int zmk_hid_mouse_button_release(zmk_mouse_button_t button); +int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons); +int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons); +void zmk_hid_mouse_movement_set(int16_t x, int16_t y); +void zmk_hid_mouse_scroll_set(int8_t x, int8_t y); +void zmk_hid_mouse_movement_update(int16_t x, int16_t y); +void zmk_hid_mouse_scroll_update(int8_t x, int8_t y); +void zmk_hid_mouse_clear(void); + +struct zmk_hid_mouse_report *zmk_mouse_hid_get_mouse_report(); diff --git a/app/include/zmk/mouse/hog.h b/app/include/zmk/mouse/hog.h new file mode 100644 index 000000000000..48a214e554ac --- /dev/null +++ b/app/include/zmk/mouse/hog.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +#if IS_ENABLED(CONFIG_ZMK_MOUSE) +int zmk_mouse_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body); +#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse/types.h similarity index 100% rename from app/include/zmk/mouse.h rename to app/include/zmk/mouse/types.h diff --git a/app/include/zmk/mouse/usb_hid.h b/app/include/zmk/mouse/usb_hid.h new file mode 100644 index 000000000000..12205f28e4c9 --- /dev/null +++ b/app/include/zmk/mouse/usb_hid.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +#if IS_ENABLED(CONFIG_ZMK_MOUSE) +int zmk_mouse_usb_hid_send_mouse_report(void); +#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/src/behaviors/behavior_mouse_key_press.c b/app/src/behaviors/behavior_mouse_key_press.c index 66b54fce05b1..8f8df2a72f36 100644 --- a/app/src/behaviors/behavior_mouse_key_press.c +++ b/app/src/behaviors/behavior_mouse_key_press.c @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include @@ -22,7 +22,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); static int behavior_mouse_key_press_init(const struct device *dev) { return 0; }; static void process_key_state(const struct device *dev, int32_t val, bool pressed) { - for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { + for (int i = 0; i < ZMK_MOUSE_HID_NUM_BUTTONS; i++) { if (val & BIT(i)) { WRITE_BIT(val, i, 0); input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER); diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 7c9d15a31fee..0bcc417356dd 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -116,9 +119,7 @@ int zmk_endpoints_toggle_transport(void) { return zmk_endpoints_select_transport(new_transport); } -struct zmk_endpoint_instance zmk_endpoints_selected(void) { - return current_instance; -} +struct zmk_endpoint_instance zmk_endpoints_selected(void) { return current_instance; } static int send_keyboard_report(void) { switch (current_instance.transport) { @@ -208,7 +209,7 @@ int zmk_endpoints_send_mouse_report() { switch (current_instance.transport) { case ZMK_TRANSPORT_USB: { #if IS_ENABLED(CONFIG_ZMK_USB) - int err = zmk_usb_hid_send_mouse_report(); + int err = zmk_mouse_usb_hid_send_mouse_report(); if (err) { LOG_ERR("FAILED TO SEND OVER USB: %d", err); } @@ -221,8 +222,8 @@ int zmk_endpoints_send_mouse_report() { case ZMK_TRANSPORT_BLE: { #if IS_ENABLED(CONFIG_ZMK_BLE) - struct zmk_hid_mouse_report *mouse_report = zmk_hid_get_mouse_report(); - int err = zmk_hog_send_mouse_report(&mouse_report->body); + struct zmk_hid_mouse_report *mouse_report = zmk_mouse_hid_get_mouse_report(); + int err = zmk_mouse_hog_send_mouse_report(&mouse_report->body); if (err) { LOG_ERR("FAILED TO SEND OVER HOG: %d", err); } diff --git a/app/src/hid.c b/app/src/hid.c index 45584a9f4892..ae2a71b16e4a 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -25,14 +25,6 @@ static uint8_t keys_held = 0; #endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */ -#if IS_ENABLED(CONFIG_ZMK_MOUSE) - -static struct zmk_hid_mouse_report mouse_report = { - .report_id = ZMK_HID_REPORT_ID_MOUSE, - .body = {.buttons = 0, .d_x = 0, .d_y = 0, .d_scroll_y = 0}}; - -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - // Keep track of how often a modifier was pressed. // Only release the modifier if the count is 0. static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -370,101 +362,6 @@ bool zmk_hid_is_pressed(uint32_t usage) { return false; } -#if IS_ENABLED(CONFIG_ZMK_MOUSE) - -// Keep track of how often a button was pressed. -// Only release the button if the count is 0. -static int explicit_button_counts[5] = {0, 0, 0, 0, 0}; -static zmk_mod_flags_t explicit_buttons = 0; - -#define SET_MOUSE_BUTTONS(btns) \ - { \ - mouse_report.body.buttons = btns; \ - LOG_DBG("Mouse buttons set to 0x%02X", mouse_report.body.buttons); \ - } - -int zmk_hid_mouse_button_press(zmk_mouse_button_t button) { - if (button >= ZMK_HID_MOUSE_NUM_BUTTONS) { - return -EINVAL; - } - - explicit_button_counts[button]++; - LOG_DBG("Button %d count %d", button, explicit_button_counts[button]); - WRITE_BIT(explicit_buttons, button, true); - SET_MOUSE_BUTTONS(explicit_buttons); - return 0; -} - -int zmk_hid_mouse_button_release(zmk_mouse_button_t button) { - if (button >= ZMK_HID_MOUSE_NUM_BUTTONS) { - return -EINVAL; - } - - if (explicit_button_counts[button] <= 0) { - LOG_ERR("Tried to release button %d too often", button); - return -EINVAL; - } - explicit_button_counts[button]--; - LOG_DBG("Button %d count: %d", button, explicit_button_counts[button]); - if (explicit_button_counts[button] == 0) { - LOG_DBG("Button %d released", button); - WRITE_BIT(explicit_buttons, button, false); - } - SET_MOUSE_BUTTONS(explicit_buttons); - return 0; -} - -int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons) { - for (zmk_mouse_button_t i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { - if (buttons & BIT(i)) { - zmk_hid_mouse_button_press(i); - } - } - return 0; -} - -int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) { - for (zmk_mouse_button_t i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { - if (buttons & BIT(i)) { - zmk_hid_mouse_button_release(i); - } - } - return 0; -} - -void zmk_hid_mouse_movement_set(int16_t x, int16_t y) { - mouse_report.body.d_x = x; - mouse_report.body.d_y = y; - LOG_DBG("Mouse movement set to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y); -} - -void zmk_hid_mouse_movement_update(int16_t x, int16_t y) { - mouse_report.body.d_x += x; - mouse_report.body.d_y += y; - LOG_DBG("Mouse movement updated to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y); -} - -void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) { - mouse_report.body.d_scroll_x = x; - mouse_report.body.d_scroll_y = y; - LOG_DBG("Mouse scroll set to %d/%d", mouse_report.body.d_scroll_x, - mouse_report.body.d_scroll_y); -} - -void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) { - mouse_report.body.d_scroll_x += x; - mouse_report.body.d_scroll_y += y; - LOG_DBG("Mouse scroll updated to X: %d/%d", mouse_report.body.d_scroll_x, - mouse_report.body.d_scroll_y); -} - -void zmk_hid_mouse_clear(void) { - LOG_DBG("Mouse report cleared"); - memset(&mouse_report.body, 0, sizeof(mouse_report.body)); -} - -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void) { return &keyboard_report; } @@ -472,11 +369,3 @@ struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void) { struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void) { return &consumer_report; } - -#if IS_ENABLED(CONFIG_ZMK_MOUSE) - -struct zmk_hid_mouse_report *zmk_hid_get_mouse_report(void) { - return &mouse_report; -} - -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/src/hog.c b/app/src/hog.c index 034302a37433..65679586e091 100644 --- a/app/src/hog.c +++ b/app/src/hog.c @@ -69,15 +69,6 @@ static struct hids_report consumer_input = { .type = HIDS_INPUT, }; -#if IS_ENABLED(CONFIG_ZMK_MOUSE) - -static struct hids_report mouse_input = { - .id = ZMK_HID_REPORT_ID_MOUSE, - .type = HIDS_INPUT, -}; - -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - static bool host_requests_notification = false; static uint8_t ctrl_point; // static uint8_t proto_mode; @@ -143,15 +134,6 @@ static ssize_t read_hids_consumer_input_report(struct bt_conn *conn, sizeof(struct zmk_hid_consumer_report_body)); } -#if IS_ENABLED(CONFIG_ZMK_MOUSE) -static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, - void *buf, uint16_t len, uint16_t offset) { - struct zmk_hid_mouse_report_body *report_body = &zmk_hid_get_mouse_report()->body; - return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, - sizeof(struct zmk_hid_mouse_report_body)); -} -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - // static ssize_t write_proto_mode(struct bt_conn *conn, // const struct bt_gatt_attr *attr, // const void *buf, uint16_t len, uint16_t offset, @@ -200,14 +182,6 @@ BT_GATT_SERVICE_DEFINE( BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref, NULL, &consumer_input), -#if IS_ENABLED(CONFIG_ZMK_MOUSE) - BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, - BT_GATT_PERM_READ_ENCRYPT, read_hids_mouse_input_report, NULL, NULL), - BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), - BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref, - NULL, &mouse_input), -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - #if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS) BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP, @@ -220,7 +194,7 @@ BT_GATT_SERVICE_DEFINE( BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point)); -struct bt_conn *destination_connection(void) { +static struct bt_conn *destination_connection(void) { struct bt_conn *conn; bt_addr_le_t *addr = zmk_ble_active_profile_addr(); LOG_DBG("Address pointer %p", addr); @@ -237,7 +211,7 @@ struct bt_conn *destination_connection(void) { K_THREAD_STACK_DEFINE(hog_q_stack, CONFIG_ZMK_BLE_THREAD_STACK_SIZE); -struct k_work_q hog_work_q; +static struct k_work_q hog_work_q; K_MSGQ_DEFINE(zmk_hog_keyboard_msgq, sizeof(struct zmk_hid_keyboard_report_body), CONFIG_ZMK_BLE_KEYBOARD_REPORT_QUEUE_SIZE, 4); @@ -343,60 +317,6 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) { return 0; }; -#if IS_ENABLED(CONFIG_ZMK_MOUSE) - -K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body), - CONFIG_ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE, 4); - -void send_mouse_report_callback(struct k_work *work) { - struct zmk_hid_mouse_report_body report; - while (k_msgq_get(&zmk_hog_mouse_msgq, &report, K_NO_WAIT) == 0) { - struct bt_conn *conn = destination_connection(); - if (conn == NULL) { - return; - } - - struct bt_gatt_notify_params notify_params = { - .attr = &hog_svc.attrs[13], - .data = &report, - .len = sizeof(report), - }; - - int err = bt_gatt_notify_cb(conn, ¬ify_params); - if (err == -EPERM) { - bt_conn_set_security(conn, BT_SECURITY_L2); - } else if (err) { - LOG_DBG("Error notifying %d", err); - } - - bt_conn_unref(conn); - } -}; - -K_WORK_DEFINE(hog_mouse_work, send_mouse_report_callback); - -int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) { - int err = k_msgq_put(&zmk_hog_mouse_msgq, report, K_MSEC(100)); - if (err) { - switch (err) { - case -EAGAIN: { - LOG_WRN("Consumer message queue full, popping first message and queueing again"); - struct zmk_hid_mouse_report_body discarded_report; - k_msgq_get(&zmk_hog_mouse_msgq, &discarded_report, K_NO_WAIT); - return zmk_hog_send_mouse_report(report); - } - default: - LOG_WRN("Failed to queue mouse report to send (%d)", err); - return err; - } - } - - k_work_submit_to_queue(&hog_work_q, &hog_mouse_work); - - return 0; -}; -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - static int zmk_hog_init(void) { static const struct k_work_queue_config queue_config = {.name = "HID Over GATT Send Work"}; k_work_queue_start(&hog_work_q, hog_q_stack, K_THREAD_STACK_SIZEOF(hog_q_stack), diff --git a/app/src/main.c b/app/src/main.c index acaf3b649cab..9bd7af327238 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -17,10 +17,6 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include -#ifdef CONFIG_ZMK_MOUSE -#include -#endif /* CONFIG_ZMK_MOUSE */ - int main(void) { LOG_INF("Welcome to ZMK!\n"); diff --git a/app/src/mouse/CMakeLists.txt b/app/src/mouse/CMakeLists.txt new file mode 100644 index 000000000000..405c10866a2e --- /dev/null +++ b/app/src/mouse/CMakeLists.txt @@ -0,0 +1,6 @@ +if(CONFIG_ZMK_MOUSE) + target_sources(app PRIVATE hid.c) + + target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE usb_hid.c) + target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE hog.c) +endif() \ No newline at end of file diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig index 2dbbf90c71ce..d2f9d5259952 100644 --- a/app/src/mouse/Kconfig +++ b/app/src/mouse/Kconfig @@ -1,8 +1,19 @@ # Copyright (c) 2023 The ZMK Contributors # SPDX-License-Identifier: MIT -config ZMK_MOUSE +menuconfig ZMK_MOUSE bool "Mouse Emulation" select INPUT select INPUT_THREAD_PRIORITY_OVERRIDE + select USB_COMPOSITE_DEVICE if ZMK_USB +if ZMK_MOUSE + +if ZMK_USB + +config USB_HID_DEVICE_COUNT + default 2 + +endif + +endif # ZMK_MOUSE \ No newline at end of file diff --git a/app/src/mouse/hid.c b/app/src/mouse/hid.c new file mode 100644 index 000000000000..556c07ee1889 --- /dev/null +++ b/app/src/mouse/hid.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include + +static struct zmk_hid_mouse_report mouse_report = { + .report_id = ZMK_MOUSE_HID_REPORT_ID_MOUSE, + .body = {.buttons = 0, .d_x = 0, .d_y = 0, .d_scroll_y = 0}}; + +// Keep track of how often a button was pressed. +// Only release the button if the count is 0. +static int explicit_button_counts[5] = {0, 0, 0, 0, 0}; +static zmk_mod_flags_t explicit_buttons = 0; + +#define SET_MOUSE_BUTTONS(btns) \ + { \ + mouse_report.body.buttons = btns; \ + LOG_DBG("Mouse buttons set to 0x%02X", mouse_report.body.buttons); \ + } + +int zmk_hid_mouse_button_press(zmk_mouse_button_t button) { + if (button >= ZMK_MOUSE_HID_NUM_BUTTONS) { + return -EINVAL; + } + + explicit_button_counts[button]++; + LOG_DBG("Button %d count %d", button, explicit_button_counts[button]); + WRITE_BIT(explicit_buttons, button, true); + SET_MOUSE_BUTTONS(explicit_buttons); + return 0; +} + +int zmk_hid_mouse_button_release(zmk_mouse_button_t button) { + if (button >= ZMK_MOUSE_HID_NUM_BUTTONS) { + return -EINVAL; + } + + if (explicit_button_counts[button] <= 0) { + LOG_ERR("Tried to release button %d too often", button); + return -EINVAL; + } + explicit_button_counts[button]--; + LOG_DBG("Button %d count: %d", button, explicit_button_counts[button]); + if (explicit_button_counts[button] == 0) { + LOG_DBG("Button %d released", button); + WRITE_BIT(explicit_buttons, button, false); + } + SET_MOUSE_BUTTONS(explicit_buttons); + return 0; +} + +int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons) { + for (zmk_mouse_button_t i = 0; i < ZMK_MOUSE_HID_NUM_BUTTONS; i++) { + if (buttons & BIT(i)) { + zmk_hid_mouse_button_press(i); + } + } + return 0; +} + +int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) { + for (zmk_mouse_button_t i = 0; i < ZMK_MOUSE_HID_NUM_BUTTONS; i++) { + if (buttons & BIT(i)) { + zmk_hid_mouse_button_release(i); + } + } + return 0; +} + +void zmk_hid_mouse_movement_set(int16_t x, int16_t y) { + mouse_report.body.d_x = x; + mouse_report.body.d_y = y; + LOG_DBG("Mouse movement set to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y); +} + +void zmk_hid_mouse_movement_update(int16_t x, int16_t y) { + mouse_report.body.d_x += x; + mouse_report.body.d_y += y; + LOG_DBG("Mouse movement updated to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y); +} + +void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) { + mouse_report.body.d_scroll_x = x; + mouse_report.body.d_scroll_y = y; + LOG_DBG("Mouse scroll set to %d/%d", mouse_report.body.d_scroll_x, + mouse_report.body.d_scroll_y); +} + +void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) { + mouse_report.body.d_scroll_x += x; + mouse_report.body.d_scroll_y += y; + LOG_DBG("Mouse scroll updated to X: %d/%d", mouse_report.body.d_scroll_x, + mouse_report.body.d_scroll_y); +} + +void zmk_hid_mouse_clear(void) { + LOG_DBG("Mouse report cleared"); + memset(&mouse_report.body, 0, sizeof(mouse_report.body)); +} + +struct zmk_hid_mouse_report *zmk_mouse_hid_get_mouse_report(void) { return &mouse_report; } diff --git a/app/src/mouse/hog.c b/app/src/mouse/hog.c new file mode 100644 index 000000000000..d68f43b091c6 --- /dev/null +++ b/app/src/mouse/hog.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include + +#include +#include +#include +#include + +enum { + HIDS_REMOTE_WAKE = BIT(0), + HIDS_NORMALLY_CONNECTABLE = BIT(1), +}; + +struct hids_info { + uint16_t version; /* version number of base USB HID Specification */ + uint8_t code; /* country HID Device hardware is localized for. */ + uint8_t flags; +} __packed; + +struct hids_report { + uint8_t id; /* report id */ + uint8_t type; /* report type */ +} __packed; + +static struct hids_info info = { + .version = 0x1101, + .code = 0x00, + .flags = HIDS_NORMALLY_CONNECTABLE | HIDS_REMOTE_WAKE, +}; + +enum { + HIDS_INPUT = 0x01, + HIDS_OUTPUT = 0x02, + HIDS_FEATURE = 0x03, +}; + +static struct hids_report mouse_input = { + .id = ZMK_MOUSE_HID_REPORT_ID_MOUSE, + .type = HIDS_INPUT, +}; + +static bool host_requests_notification = false; +static uint8_t ctrl_point; + +static ssize_t read_hids_info(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) { + return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data, + sizeof(struct hids_info)); +} + +static ssize_t read_hids_report_ref(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) { + return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data, + sizeof(struct hids_report)); +} + +static ssize_t read_hids_report_map(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) { + return bt_gatt_attr_read(conn, attr, buf, len, offset, zmk_mouse_hid_report_desc, + sizeof(zmk_mouse_hid_report_desc)); +} + +static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) { + struct zmk_hid_mouse_report_body *report_body = &zmk_mouse_hid_get_mouse_report()->body; + return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, + sizeof(struct zmk_hid_mouse_report_body)); +} + +static void input_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value) { + LOG_DBG("Input CC changed for %d", attr->handle); + host_requests_notification = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0; +} + +static ssize_t write_ctrl_point(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + uint8_t *value = attr->user_data; + + if (offset + len > sizeof(ctrl_point)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + memcpy(value + offset, buf, len); + + return len; +} + +/* HID Service Declaration */ +BT_GATT_SERVICE_DEFINE( + mouse_hog_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_HIDS), + + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT_MAP, BT_GATT_CHRC_READ, BT_GATT_PERM_READ_ENCRYPT, + read_hids_report_map, NULL, NULL), + + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ_ENCRYPT, read_hids_mouse_input_report, NULL, NULL), + BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), + BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref, + NULL, &mouse_input), + + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_INFO, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, read_hids_info, + NULL, &info), + + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point)); + +static struct bt_conn *destination_connection(void) { + struct bt_conn *conn; + bt_addr_le_t *addr = zmk_ble_active_profile_addr(); + + if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { + LOG_WRN("Not sending, no active address for current profile"); + return NULL; + } else if ((conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr)) == NULL) { + LOG_WRN("Not sending, not connected to active profile"); + return NULL; + } + + return conn; +} + +K_THREAD_STACK_DEFINE(mouse_hog_q_stack, CONFIG_ZMK_BLE_THREAD_STACK_SIZE); + +static struct k_work_q mouse_hog_work_q; + +K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body), + CONFIG_ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE, 4); + +void send_mouse_report_callback(struct k_work *work) { + struct zmk_hid_mouse_report_body report; + while (k_msgq_get(&zmk_hog_mouse_msgq, &report, K_NO_WAIT) == 0) { + struct bt_conn *conn = destination_connection(); + if (conn == NULL) { + return; + } + + struct bt_gatt_notify_params notify_params = { + .attr = &mouse_hog_svc.attrs[5], + .data = &report, + .len = sizeof(report), + }; + + int err = bt_gatt_notify_cb(conn, ¬ify_params); + if (err == -EPERM) { + bt_conn_set_security(conn, BT_SECURITY_L2); + } else if (err) { + LOG_DBG("Error notifying %d", err); + } + + bt_conn_unref(conn); + } +}; + +K_WORK_DEFINE(hog_mouse_work, send_mouse_report_callback); + +int zmk_mouse_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) { + int err = k_msgq_put(&zmk_hog_mouse_msgq, report, K_MSEC(100)); + if (err) { + switch (err) { + case -EAGAIN: { + LOG_WRN("Consumer message queue full, popping first message and queueing again"); + struct zmk_hid_mouse_report_body discarded_report; + k_msgq_get(&zmk_hog_mouse_msgq, &discarded_report, K_NO_WAIT); + return zmk_mouse_hog_send_mouse_report(report); + } + default: + LOG_WRN("Failed to queue mouse report to send (%d)", err); + return err; + } + } + + k_work_submit_to_queue(&mouse_hog_work_q, &hog_mouse_work); + + return 0; +}; + +static int zmk_mouse_hog_init(void) { + static const struct k_work_queue_config queue_config = {.name = + "Mouse HID Over GATT Send Work"}; + k_work_queue_start(&mouse_hog_work_q, mouse_hog_q_stack, + K_THREAD_STACK_SIZEOF(mouse_hog_q_stack), CONFIG_ZMK_BLE_THREAD_PRIORITY, + &queue_config); + + return 0; +} + +SYS_INIT(zmk_mouse_hog_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY); diff --git a/app/src/mouse/input_listener.c b/app/src/mouse/input_listener.c index 6863e00eac41..4ddca057c3ab 100644 --- a/app/src/mouse/input_listener.c +++ b/app/src/mouse/input_listener.c @@ -9,11 +9,15 @@ #include #include #include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + #include -#include #include -#include +#include +#include #define ONE_IF_DEV_OK(n) \ COND_CODE_1(DT_NODE_HAS_STATUS(DT_INST_PHANDLE(n, device), okay), (1 +), (0 +)) @@ -35,11 +39,15 @@ struct input_listener_xy_data { }; struct input_listener_data { - struct input_listener_xy_data data; - struct input_listener_xy_data wheel_data; - - uint8_t button_set; - uint8_t button_clear; + union { + struct { + struct input_listener_xy_data data; + struct input_listener_xy_data wheel_data; + + uint8_t button_set; + uint8_t button_clear; + } mouse; + }; }; struct input_listener_config { @@ -53,27 +61,31 @@ struct input_listener_config { static void handle_rel_code(struct input_listener_data *data, struct input_event *evt) { switch (evt->code) { case INPUT_REL_X: - data->data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; - data->data.x += evt->value; + data->mouse.data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->mouse.data.x += evt->value; break; case INPUT_REL_Y: - data->data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; - data->data.y += evt->value; + data->mouse.data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->mouse.data.y += evt->value; break; case INPUT_REL_WHEEL: - data->wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; - data->wheel_data.y += evt->value; + data->mouse.wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->mouse.wheel_data.y += evt->value; break; case INPUT_REL_HWHEEL: - data->wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; - data->wheel_data.x += evt->value; + data->mouse.wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->mouse.wheel_data.x += evt->value; break; default: break; } } -static void handle_key_code(struct input_listener_data *data, struct input_event *evt) { +static void handle_abs_code(const struct input_listener_config *config, + struct input_listener_data *data, struct input_event *evt) {} + +static void handle_key_code(const struct input_listener_config *config, + struct input_listener_data *data, struct input_event *evt) { int8_t btn; switch (evt->code) { @@ -84,9 +96,9 @@ static void handle_key_code(struct input_listener_data *data, struct input_event case INPUT_BTN_4: btn = evt->code - INPUT_BTN_0; if (evt->value > 0) { - WRITE_BIT(data->button_set, btn, 1); + WRITE_BIT(data->mouse.button_set, btn, 1); } else { - WRITE_BIT(data->button_clear, btn, 1); + WRITE_BIT(data->mouse.button_clear, btn, 1); } break; default: @@ -105,6 +117,14 @@ static void swap_xy(struct input_event *evt) { } } +static inline bool is_x_data(const struct input_event *evt) { + return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_X; +} + +static inline bool is_y_data(const struct input_event *evt) { + return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_Y; +} + static void filter_with_input_config(const struct input_listener_config *cfg, struct input_event *evt) { if (!evt->dev) { @@ -115,8 +135,7 @@ static void filter_with_input_config(const struct input_listener_config *cfg, swap_xy(evt); } - if ((cfg->x_invert && evt->code == INPUT_REL_X) || - (cfg->y_invert && evt->code == INPUT_REL_Y)) { + if ((cfg->x_invert && is_x_data(evt)) || (cfg->y_invert && is_y_data(evt))) { evt->value = -(evt->value); } @@ -137,31 +156,34 @@ static void input_handler(const struct input_listener_config *config, case INPUT_EV_REL: handle_rel_code(data, evt); break; + case INPUT_EV_ABS: + handle_abs_code(config, data, evt); + break; case INPUT_EV_KEY: - handle_key_code(data, evt); + handle_key_code(config, data, evt); break; } if (evt->sync) { - if (data->wheel_data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { - zmk_hid_mouse_scroll_set(data->wheel_data.x, data->wheel_data.y); + if (data->mouse.wheel_data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { + zmk_hid_mouse_scroll_set(data->mouse.wheel_data.x, data->mouse.wheel_data.y); } - if (data->data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { - zmk_hid_mouse_movement_set(data->data.x, data->data.y); + if (data->mouse.data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { + zmk_hid_mouse_movement_set(data->mouse.data.x, data->mouse.data.y); } - if (data->button_set != 0) { - for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { - if ((data->button_set & BIT(i)) != 0) { + if (data->mouse.button_set != 0) { + for (int i = 0; i < ZMK_MOUSE_HID_NUM_BUTTONS; i++) { + if ((data->mouse.button_set & BIT(i)) != 0) { zmk_hid_mouse_button_press(i); } } } - if (data->button_clear != 0) { - for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { - if ((data->button_clear & BIT(i)) != 0) { + if (data->mouse.button_clear != 0) { + for (int i = 0; i < ZMK_MOUSE_HID_NUM_BUTTONS; i++) { + if ((data->mouse.button_clear & BIT(i)) != 0) { zmk_hid_mouse_button_release(i); } } @@ -171,10 +193,10 @@ static void input_handler(const struct input_listener_config *config, zmk_hid_mouse_scroll_set(0, 0); zmk_hid_mouse_movement_set(0, 0); - clear_xy_data(&data->data); - clear_xy_data(&data->wheel_data); + clear_xy_data(&data->mouse.data); + clear_xy_data(&data->mouse.wheel_data); - data->button_set = data->button_clear = 0; + data->mouse.button_set = data->mouse.button_clear = 0; } } diff --git a/app/src/mouse/usb_hid.c b/app/src/mouse/usb_hid.c new file mode 100644 index 000000000000..1bfdd4608cec --- /dev/null +++ b/app/src/mouse/usb_hid.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +static const struct device *hid_dev; + +static K_SEM_DEFINE(hid_sem, 1, 1); + +static void in_ready_cb(const struct device *dev) { k_sem_give(&hid_sem); } + +#define HID_GET_REPORT_TYPE_MASK 0xff00 +#define HID_GET_REPORT_ID_MASK 0x00ff + +#define HID_REPORT_TYPE_INPUT 0x100 +#define HID_REPORT_TYPE_OUTPUT 0x200 +#define HID_REPORT_TYPE_FEATURE 0x300 + +#if IS_ENABLED(CONFIG_ZMK_USB_BOOT) +static uint8_t hid_protocol = HID_PROTOCOL_REPORT; + +static void set_proto_cb(const struct device *dev, uint8_t protocol) { hid_protocol = protocol; } + +#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */ + +static int get_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len, + uint8_t **data) { + + /* + * 7.2.1 of the HID v1.11 spec is unclear about handling requests for reports that do not exist + * For requested reports that aren't input reports, return -ENOTSUP like the Zephyr subsys does + */ + if ((setup->wValue & HID_GET_REPORT_TYPE_MASK) != HID_REPORT_TYPE_INPUT && + (setup->wValue & HID_GET_REPORT_TYPE_MASK) != HID_REPORT_TYPE_FEATURE) { + LOG_ERR("Get: Unsupported report type %d requested", + (setup->wValue & HID_GET_REPORT_TYPE_MASK) >> 8); + return -ENOTSUP; + } + + switch (setup->wValue & HID_GET_REPORT_ID_MASK) { + case ZMK_MOUSE_HID_REPORT_ID_MOUSE: + struct zmk_hid_mouse_report *report = zmk_mouse_hid_get_mouse_report(); + *data = (uint8_t *)report; + *len = sizeof(*report); + break; + default: + LOG_ERR("Invalid report ID %d requested", setup->wValue & HID_GET_REPORT_ID_MASK); + return -EINVAL; + } + + return 0; +} + +static int set_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len, + uint8_t **data) { + if ((setup->wValue & HID_GET_REPORT_TYPE_MASK) != HID_REPORT_TYPE_OUTPUT && + (setup->wValue & HID_GET_REPORT_TYPE_MASK) != HID_REPORT_TYPE_FEATURE) { + LOG_ERR("Set: Unsupported report type %d requested", + (setup->wValue & HID_GET_REPORT_TYPE_MASK) >> 8); + return -ENOTSUP; + } + + switch (setup->wValue & HID_GET_REPORT_ID_MASK) { + default: + LOG_ERR("Invalid report ID %d requested", setup->wValue & HID_GET_REPORT_ID_MASK); + return -EINVAL; + } + + return 0; +} + +static const struct hid_ops ops = { +#if IS_ENABLED(CONFIG_ZMK_USB_BOOT) + .protocol_change = set_proto_cb, +#endif + .int_in_ready = in_ready_cb, + .get_report = get_report_cb, + .set_report = set_report_cb, +}; + +static int zmk_mouse_usb_hid_send_report(const uint8_t *report, size_t len) { + switch (zmk_usb_get_status()) { + case USB_DC_SUSPEND: + return usb_wakeup_request(); + case USB_DC_ERROR: + case USB_DC_RESET: + case USB_DC_DISCONNECTED: + case USB_DC_UNKNOWN: + return -ENODEV; + default: + k_sem_take(&hid_sem, K_MSEC(30)); + LOG_HEXDUMP_DBG(report, len, "Mouse HID report"); + int err = hid_int_ep_write(hid_dev, report, len, NULL); + + if (err) { + LOG_ERR("Failed to write %d", err); + k_sem_give(&hid_sem); + } + + return err; + } +} + +#if IS_ENABLED(CONFIG_ZMK_MOUSE) +int zmk_mouse_usb_hid_send_mouse_report() { +#if IS_ENABLED(CONFIG_ZMK_USB_BOOT) + if (hid_protocol == HID_PROTOCOL_BOOT) { + return -ENOTSUP; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */ + + struct zmk_hid_mouse_report *report = zmk_mouse_hid_get_mouse_report(); + return zmk_mouse_usb_hid_send_report((uint8_t *)report, sizeof(*report)); +} +#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) + +static int zmk_mouse_usb_hid_init(void) { + hid_dev = device_get_binding("HID_1"); + if (hid_dev == NULL) { + LOG_ERR("Unable to locate HID device"); + return -EINVAL; + } + + usb_hid_register_device(hid_dev, zmk_mouse_hid_report_desc, sizeof(zmk_mouse_hid_report_desc), + &ops); + + // usb_hid_set_proto_code(hid_dev, HID_BOOT_IFACE_CODE_MOUSE); + + usb_hid_init(hid_dev); + + return 0; +} + +SYS_INIT(zmk_mouse_usb_hid_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/app/src/usb_hid.c b/app/src/usb_hid.c index cd3ef9203910..383aeb812724 100644 --- a/app/src/usb_hid.c +++ b/app/src/usb_hid.c @@ -164,19 +164,6 @@ int zmk_usb_hid_send_consumer_report(void) { return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report)); } -#if IS_ENABLED(CONFIG_ZMK_MOUSE) -int zmk_usb_hid_send_mouse_report() { -#if IS_ENABLED(CONFIG_ZMK_USB_BOOT) - if (hid_protocol == HID_PROTOCOL_BOOT) { - return -ENOTSUP; - } -#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */ - - struct zmk_hid_mouse_report *report = zmk_hid_get_mouse_report(); - return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report)); -} -#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) - static int zmk_usb_hid_init(void) { hid_dev = device_get_binding("HID_0"); if (hid_dev == NULL) {