From 6861dc758f5d3778fb63db9e816c3e6db934861d 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. * Add PTP bits for multitouch. --- app/CMakeLists.txt | 1 + app/dts/bindings/zmk,input-listener.yaml | 6 + app/include/zmk/endpoints.h | 4 + app/include/zmk/hid.h | 77 ---- app/include/zmk/hog.h | 4 - app/include/zmk/mouse/hid.h | 378 +++++++++++++++++++ app/include/zmk/mouse/hog.h | 17 + app/include/zmk/{mouse.h => mouse/types.h} | 0 app/include/zmk/mouse/usb_hid.h | 17 + app/src/behaviors/behavior_mouse_key_press.c | 4 +- app/src/endpoints.c | 46 ++- 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 | 50 ++- app/src/mouse/hid.c | 229 +++++++++++ app/src/mouse/hog.c | 352 +++++++++++++++++ app/src/mouse/input_listener.c | 171 +++++++-- app/src/mouse/usb_hid.c | 221 +++++++++++ app/src/usb_hid.c | 13 - 21 files changed, 1458 insertions(+), 337 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 916455bf9776..0655550a37d7 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -99,5 +99,6 @@ target_sources_ifdef(CONFIG_ZMK_LOW_PRIORITY_WORK_QUEUE app PRIVATE src/workqueu target_sources(app PRIVATE src/main.c) add_subdirectory(src/display/) +add_subdirectory(src/mouse/) zephyr_cc_option(-Wfatal-errors) diff --git a/app/dts/bindings/zmk,input-listener.yaml b/app/dts/bindings/zmk,input-listener.yaml index a883557db396..eb6e322ac4ee 100644 --- a/app/dts/bindings/zmk,input-listener.yaml +++ b/app/dts/bindings/zmk,input-listener.yaml @@ -4,6 +4,12 @@ description: | compatible: "zmk,input-listener" properties: + mode: + type: string + default: "mouse" + enum: + - "mouse" + - "trackpad" device: type: phandle required: true diff --git a/app/include/zmk/endpoints.h b/app/include/zmk/endpoints.h index b4eac1f38c43..a38ea90d6619 100644 --- a/app/include/zmk/endpoints.h +++ b/app/include/zmk/endpoints.h @@ -73,3 +73,7 @@ int zmk_endpoints_send_report(uint16_t usage_page); #if IS_ENABLED(CONFIG_ZMK_MOUSE) int zmk_endpoints_send_mouse_report(); #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) +int zmk_endpoints_send_ptp_report(); +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) \ No newline at end of file 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..51e150f65745 --- /dev/null +++ b/app/include/zmk/mouse/hid.h @@ -0,0 +1,378 @@ +/* + * 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 + +// Windows PTP Collection +// (https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchpad-windows-precision-touchpad-collection) +#define ZMK_MOUSE_HID_REPORT_ID_DIGITIZER 0x08 +#define ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_CAPABILITIES 0x04 +#define ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTPHQA 0x05 + +// Configuration Collection +// (https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchpad-configuration-collection) +#define ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_MODE 0x06 +#define ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_SELECTIVE 0x07 + +#define ZMK_HID_USAGE_PAGE16(page) \ + HID_ITEM(HID_ITEM_TAG_USAGE_PAGE, HID_ITEM_TYPE_GLOBAL, 2), (page & 0xFF), (page >> 8 & 0xFF) + +#define ZMK_HID_LOGICAL_MIN32(a) \ + HID_ITEM(HID_ITEM_TAG_LOGICAL_MIN, HID_ITEM_TYPE_GLOBAL, 3), (a & 0xFF), (a >> 8 & 0xFF), \ + (a >> 16 & 0xFF), (a >> 24 & 0xFF) +#define ZMK_HID_LOGICAL_MAX32(a) \ + HID_ITEM(HID_ITEM_TAG_LOGICAL_MAX, HID_ITEM_TYPE_GLOBAL, 3), (a & 0xFF), (a >> 8 & 0xFF), \ + (a >> 16 & 0xFF), (a >> 24 & 0xFF) + +#define ZMK_HID_PHYSICAL_MAX32(a) \ + HID_ITEM(HID_ITEM_TAG_PHYSICAL_MAX, HID_ITEM_TYPE_GLOBAL, 3), (a & 0xFF), (a >> 8 & 0xFF), \ + (a >> 16 & 0xFF), (a >> 24 & 0xFF) + +#define ZMK_HID_PHYSICAL_MIN8(a) HID_ITEM(HID_ITEM_TAG_PHYSICAL_MIN, HID_ITEM_TYPE_GLOBAL, 1), a +#define ZMK_HID_PHYSICAL_MAX8(a) HID_ITEM(HID_ITEM_TAG_PHYSICAL_MAX, HID_ITEM_TYPE_GLOBAL, 1), a +#define ZMK_HID_PHYSICAL_MAX16(a) \ + HID_ITEM(HID_ITEM_TAG_PHYSICAL_MAX, HID_ITEM_TYPE_GLOBAL, 2), (a & 0xFF), (a >> 8 & 0xFF) + +#define HID_REPORT_COUNT16(count) \ + HID_ITEM(HID_ITEM_TAG_REPORT_COUNT, HID_ITEM_TYPE_GLOBAL, 2), (count & 0xFF), \ + (count >> 8 & 0xFF) + +#define ZMK_HID_EXPONENT8(a) HID_ITEM(HID_ITEM_TAG_UNIT_EXPONENT, HID_ITEM_TYPE_GLOBAL, 1), a + +#define ZMK_HID_UNIT8(a) HID_ITEM(HID_ITEM_TAG_UNIT, HID_ITEM_TYPE_GLOBAL, 1), a +#define ZMK_HID_UNIT16(a) \ + HID_ITEM(HID_ITEM_TAG_UNIT, HID_ITEM_TYPE_GLOBAL, 2), (a & 0xFF), (a >> 8 & 0xFF) + +#define TRACKPAD_FINGER_DESC(n, c) \ + HID_USAGE(HID_USAGE_DIGITIZERS_FINGER), HID_COLLECTION(HID_COLLECTION_LOGICAL), \ + HID_LOGICAL_MIN8(0), HID_LOGICAL_MAX8(1), HID_USAGE(HID_USAGE_DIGITIZERS_TOUCH_VALID), \ + HID_USAGE(HID_USAGE_DIGITIZERS_TIP_SWITCH), HID_REPORT_COUNT(2), HID_REPORT_SIZE(1), \ + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | \ + ZMK_HID_MAIN_VAL_ABS), /* Filler Bits */ \ + HID_REPORT_SIZE(1), HID_REPORT_COUNT(6), \ + HID_INPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_ARRAY | ZMK_HID_MAIN_VAL_ABS), \ + \ + HID_REPORT_COUNT(1), HID_REPORT_SIZE(3), HID_LOGICAL_MAX8(CONFIG_ZMK_TRACKPAD_FINGERS), \ + HID_USAGE(HID_USAGE_DIGITIZERS_CONTACT_IDENTIFIER), \ + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | \ + ZMK_HID_MAIN_VAL_ABS), /* Filler Bits */ \ + HID_REPORT_SIZE(1), HID_REPORT_COUNT(5), \ + HID_INPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_ARRAY | ZMK_HID_MAIN_VAL_ABS), \ + \ + HID_USAGE_PAGE(HID_USAGE_GD), HID_LOGICAL_MIN8(0), \ + HID_LOGICAL_MAX16((CONFIG_ZMK_TRACKPAD_LOGICAL_X & 0xFF), \ + ((CONFIG_ZMK_TRACKPAD_LOGICAL_X >> 8) & 0xFF)), \ + HID_REPORT_SIZE(16), /* Exponent (-2) */ \ + ZMK_HID_EXPONENT8(0x0E), /* Unit (Linear CM) */ \ + ZMK_HID_UNIT8(0x11), HID_USAGE(HID_USAGE_GD_X), ZMK_HID_PHYSICAL_MIN8(0x00), \ + ZMK_HID_PHYSICAL_MAX16(CONFIG_ZMK_TRACKPAD_PHYSICAL_X), HID_REPORT_COUNT(1), \ + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), \ + HID_LOGICAL_MAX16((CONFIG_ZMK_TRACKPAD_LOGICAL_Y & 0xFF), \ + ((CONFIG_ZMK_TRACKPAD_LOGICAL_Y >> 8) & 0xFF)), \ + ZMK_HID_PHYSICAL_MAX16(CONFIG_ZMK_TRACKPAD_PHYSICAL_Y), HID_USAGE(HID_USAGE_GD_Y), \ + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), \ + HID_END_COLLECTION, + +static const uint8_t zmk_mouse_hid_report_desc[] = { +#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_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_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_TRACKPAD) + HID_USAGE_PAGE(HID_USAGE_DIGITIZERS), + HID_USAGE(HID_USAGE_DIGITIZERS_TOUCH_PAD), + HID_COLLECTION(HID_COLLECTION_APPLICATION), + + // Windows Precision Touchpad Input Reports + HID_REPORT_ID(ZMK_MOUSE_HID_REPORT_ID_DIGITIZER), + + LISTIFY(CONFIG_ZMK_TRACKPAD_FINGERS, TRACKPAD_FINGER_DESC, + (HID_USAGE_PAGE(HID_USAGE_DIGITIZERS), )) + + HID_USAGE_PAGE(HID_USAGE_DIGITIZERS), + HID_USAGE(HID_USAGE_DIGITIZERS_SCAN_TIME), + /* Exponent (-4) */ + ZMK_HID_EXPONENT8(0x0C), + /* Unit (Linear Seconds) */ + ZMK_HID_UNIT16(0x1001), + ZMK_HID_PHYSICAL_MAX32(0xFFFF), + ZMK_HID_LOGICAL_MAX32(0xFFFF), + HID_REPORT_SIZE(16), + HID_REPORT_COUNT(1), + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + + // Contact Count + HID_USAGE(HID_USAGE_DIGITIZERS_CONTACT_COUNT), + + ZMK_HID_PHYSICAL_MAX8(0x00), + HID_LOGICAL_MAX8(0x05), + + HID_REPORT_COUNT(1), + HID_REPORT_SIZE(8), + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + + // Button report + HID_USAGE_PAGE(HID_USAGE_GEN_BUTTON), + // Buttons usages (1, 2, 3) + HID_USAGE(0x01), + HID_USAGE(0x02), + HID_USAGE(0x03), + + HID_LOGICAL_MAX8(1), + HID_REPORT_SIZE(1), + HID_REPORT_COUNT(3), + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + // Byte Padding + HID_REPORT_COUNT(5), + HID_INPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_ARRAY | ZMK_HID_MAIN_VAL_ABS), + + // Device Capabilities Feature Report + + HID_USAGE_PAGE(HID_USAGE_DIGITIZERS), + HID_REPORT_ID(ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_CAPABILITIES), + HID_USAGE(HID_USAGE_DIGITIZERS_CONTACT_COUNT_MAXIMUM), + HID_USAGE(HID_USAGE_DIGITIZERS_PAD_TYPE), + HID_REPORT_SIZE(4), + HID_REPORT_COUNT(2), + HID_LOGICAL_MAX8(0x0F), + HID_FEATURE(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + + // PTPHQA Blob: Necessary for < Windows 10 + + // USAGE_PAGE (Vendor Defined) + ZMK_HID_USAGE_PAGE16(0x00FF), + HID_COLLECTION(HID_COLLECTION_LOGICAL), + HID_REPORT_ID(ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTPHQA), + // Vendor Usage (0xC5) + HID_USAGE(0xC5), + + HID_LOGICAL_MIN8(0), + HID_LOGICAL_MAX16(0xFF, 0x00), + HID_REPORT_SIZE(8), + + // Report Count (256) + HID_REPORT_COUNT16(0x0100), + + HID_FEATURE(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + HID_END_COLLECTION, + HID_END_COLLECTION, + + // // Configuration collection + HID_USAGE_PAGE(HID_USAGE_DIGITIZERS), + HID_USAGE(HID_USAGE_DIGITIZERS_DEVICE_CONFIGURATION), + HID_COLLECTION(HID_COLLECTION_APPLICATION), + + // PTP input mode report + HID_REPORT_ID(ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_MODE), + HID_USAGE(HID_USAGE_DIGITIZERS_FINGER), + HID_COLLECTION(HID_COLLECTION_LOGICAL), + HID_USAGE(HID_USAGE_DIGITIZERS_DEVICE_MODE), + HID_LOGICAL_MIN8(0), + HID_LOGICAL_MAX8(10), + HID_REPORT_SIZE(8), + HID_REPORT_COUNT(1), + HID_FEATURE(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + HID_END_COLLECTION, + + // PTP Selective reporting report + HID_USAGE(HID_USAGE_DIGITIZERS_FINGER), + HID_COLLECTION(HID_COLLECTION_PHYSICAL), + HID_REPORT_ID(ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_SELECTIVE), + + HID_USAGE(HID_USAGE_DIGITIZERS_SURFACE_SWITCH), + HID_USAGE(HID_USAGE_DIGITIZERS_BUTTON_SWITCH), + HID_REPORT_SIZE(1), + HID_REPORT_COUNT(2), + + HID_LOGICAL_MIN8(0), + HID_LOGICAL_MAX8(1), + HID_FEATURE(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + // Byte Padding + HID_REPORT_COUNT(6), + HID_FEATURE(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS), + HID_END_COLLECTION, + HID_END_COLLECTION, +#endif +}; + +#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) + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) +// Reporting structure from osmakari +// Report for single finger +struct zmk_ptp_finger { + // Touch Valid (bit 0) and tip switch (bit 1) + uint8_t valid_tip; + // Contact ID + uint8_t contact_id; + // X + uint16_t x; + // Y + uint16_t y; +} __packed; + +struct zmk_hid_ptp_report_body { + // Finger reporting + struct zmk_ptp_finger fingers[CONFIG_ZMK_TRACKPAD_FINGERS]; + // scantime + uint16_t scan_time; + // Contact count + uint8_t contact_count; + // Buttons + uint8_t buttons; +} __packed; + +// Report containing finger data +struct zmk_hid_ptp_report { + uint8_t report_id; + struct zmk_hid_ptp_report_body body; +} __packed; + +// Feature report for configuration + +struct zmk_hid_ptp_feature_selective_report_body { + // Selective reporting: Surface switch (bit 0), Button switch (bit 1) + uint8_t surface_switch : 1; + uint8_t button_switch : 1; + uint8_t padding : 6; +} __packed; + +struct zmk_hid_ptp_feature_selective_report { + uint8_t report_id; + struct zmk_hid_ptp_feature_selective_report_body body; +} __packed; + +// Feature report for mode +struct zmk_hid_ptp_feature_mode_report { + uint8_t report_id; + // input mode, 0 for mouse, 3 for trackpad + uint8_t mode; +} __packed; + +// Feature report for certification +struct zmk_hid_ptp_feature_certification_report { + uint8_t report_id; + + uint8_t ptphqa_blob[256]; +} __packed; + +#define PTP_PAD_TYPE_DEPRESSIBLE 0x00 +#define PTP_PAD_TYPE_PRESSURE 0x01 +#define PTP_PAD_TYPE_NON_CLICKABLE 0x02 + +// Feature report for device capabilities +struct zmk_hid_ptp_feature_capabilities_report_body { + // Max touches (L 4bit) and pad type (H 4bit): + // Max touches: number 3-5 + // Pad type: 0 for Depressible, 1 for Non-depressible, 2 for Non-clickable + uint8_t max_touches : 4; + uint8_t pad_type : 4; +} __packed; + +// Feature report for device capabilities +struct zmk_hid_ptp_feature_capabilities_report { + uint8_t report_id; + struct zmk_hid_ptp_feature_capabilities_report_body body; +} __packed; + +#endif + +#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); + +struct zmk_hid_mouse_report *zmk_mouse_hid_get_mouse_report(); +#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +int zmk_mouse_hid_set_ptp_finger(struct zmk_ptp_finger finger); +void zmk_mouse_hid_ptp_update_scan_time(void); +void zmk_mouse_hid_ptp_clear(void); + +struct zmk_hid_ptp_report *zmk_mouse_hid_get_ptp_report(); + +struct zmk_hid_ptp_feature_selective_report *zmk_mouse_hid_ptp_get_feature_selective_report(); +void zmk_mouse_hid_ptp_set_feature_selective_report(bool surface_switch, bool button_switch); +struct zmk_hid_ptp_feature_mode_report *zmk_mouse_hid_ptp_get_feature_mode_report(); +void zmk_mouse_hid_ptp_set_feature_mode(uint8_t mode); +struct zmk_hid_ptp_feature_certification_report * +zmk_mouse_hid_ptp_get_feature_certification_report(); +struct zmk_hid_ptp_feature_capabilities_report *zmk_mouse_hid_ptp_get_feature_capabilities_report(); + +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) diff --git a/app/include/zmk/mouse/hog.h b/app/include/zmk/mouse/hog.h new file mode 100644 index 000000000000..422ed284134d --- /dev/null +++ b/app/include/zmk/mouse/hog.h @@ -0,0 +1,17 @@ +/* + * 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) + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) +int zmk_mouse_hog_send_ptp_report(struct zmk_hid_ptp_report *body); +#endif // IS_ENABLED(CONFIG_ZMK_TRAACKPAD) \ No newline at end of file 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..736ae1aac856 --- /dev/null +++ b/app/include/zmk/mouse/usb_hid.h @@ -0,0 +1,17 @@ +/* + * 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) + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) +int zmk_mouse_usb_hid_send_ptp_report(void); +#endif // IS_ENABLED(CONFIG_ZMK_TRAACKPAD) \ No newline at end of file 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 f8452d93fbb0..2aafacf69194 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -208,7 +211,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 +224,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); } @@ -239,6 +242,43 @@ int zmk_endpoints_send_mouse_report() { } #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) +int zmk_endpoints_send_ptp_report() { + switch (current_instance.transport) { + case ZMK_TRANSPORT_USB: { +#if IS_ENABLED(CONFIG_ZMK_USB) + LOG_DBG("Send PTP USB REPORT"); + int err = zmk_mouse_usb_hid_send_ptp_report(); + if (err) { + LOG_ERR("FAILED TO SEND OVER USB: %d", err); + } + return err; +#else + LOG_ERR("USB endpoint is not supported"); + return -ENOTSUP; +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ + } + + case ZMK_TRANSPORT_BLE: { +#if IS_ENABLED(CONFIG_ZMK_BLE) + struct zmk_hid_ptp_report *mouse_report = zmk_mouse_hid_get_ptp_report(); + int err = zmk_mouse_hog_send_ptp_report(&mouse_report->body); + if (err) { + LOG_ERR("FAILED TO SEND OVER HOG: %d", err); + } + return err; +#else + LOG_ERR("BLE HOG endpoint is not supported"); + return -ENOTSUP; +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + } + } + + LOG_ERR("Unhandled endpoint transport %d", current_instance.transport); + return -ENOTSUP; +} +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + #if IS_ENABLED(CONFIG_SETTINGS) static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb, 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..afe28c932a0a 100644 --- a/app/src/mouse/Kconfig +++ b/app/src/mouse/Kconfig @@ -1,8 +1,56 @@ # 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 + +if ZMK_MOUSE + +if ZMK_USB + +config USB_HID_DEVICE_COUNT + default 2 + +endif + +config ZMK_TRACKPAD + bool "Trackpad support" + +if ZMK_TRACKPAD + +if ZMK_USB + +config HID_INTERRUPT_EP_MPS + default 320 + +config ENABLE_HID_INT_OUT_EP + default y + +endif + +config ZMK_TRACKPAD_FINGERS + int "Max simultaneous fingers" + default 5 + +config ZMK_TRACKPAD_LOGICAL_X + int "Logical X" + default 256 + +config ZMK_TRACKPAD_LOGICAL_Y + int "Logical Y" + default 256 + +config ZMK_TRACKPAD_PHYSICAL_X + int "Physical X" + default 256 + +config ZMK_TRACKPAD_PHYSICAL_Y + int "Physical Y" + default 256 + +endif # ZMK_TRACKPAD + +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..e0f4bc10eb49 --- /dev/null +++ b/app/src/mouse/hid.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include + +#if IS_ENABLED(CONFIG_ZMK_MOUSE) + +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; +} + +#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +#define FINGER_INIT(idx, _rest) \ + { .contact_id = idx } + +static struct zmk_hid_ptp_report ptp_report = { + .report_id = ZMK_MOUSE_HID_REPORT_ID_DIGITIZER, +}; + +int zmk_mouse_hid_set_ptp_finger(struct zmk_ptp_finger finger) { + bool finger_removed = false; + for (int i = 0; i < ptp_report.body.contact_count; i++) { + if (ptp_report.body.fingers[i].contact_id == finger.contact_id) { + ptp_report.body.fingers[i] = finger; + + if (!finger.valid_tip) { + finger_removed = true; + } else { + return 0; + } + } else if (finger_removed) { + ptp_report.body.fingers[i - 1] = ptp_report.body.fingers[i]; + } + } + + if (finger_removed) { + memset(&ptp_report.body.fingers[--ptp_report.body.contact_count], 0, + sizeof(struct zmk_ptp_finger)); + return 0; + } + + if (ptp_report.body.contact_count == CONFIG_ZMK_TRACKPAD_FINGERS) { + return -ENOMEM; + } + + ptp_report.body.fingers[ptp_report.body.contact_count++] = finger; + + return 0; +} + +void zmk_mouse_hid_ptp_update_scan_time(void) { + if (ptp_report.body.contact_count > 0) { + // scan time is in 100 microsecond units + ptp_report.body.scan_time = (uint16_t)((k_uptime_get_32() * 10) & 0xFFFF); + } else { + ptp_report.body.scan_time = 0; + } +} + +void zmk_mouse_hid_ptp_clear(void) { + memset(&ptp_report.body, sizeof(struct zmk_hid_ptp_report_body), 0); +} + +struct zmk_hid_ptp_report *zmk_mouse_hid_get_ptp_report() { + return &ptp_report; +} + +struct zmk_hid_ptp_feature_selective_report *zmk_mouse_hid_ptp_get_feature_selective_report(); +void zmk_mouse_hid_ptp_set_feature_selective_report(bool surface_switch, bool button_switch); + +struct zmk_hid_ptp_feature_mode_report mode_report = { + .report_id = ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_MODE, + .mode = 0, +}; + +struct zmk_hid_ptp_feature_mode_report *zmk_mouse_hid_ptp_get_feature_mode_report() { + return &mode_report; +} + +void zmk_mouse_hid_ptp_set_feature_mode(uint8_t mode) { mode_report.mode = mode; } + +static struct zmk_hid_ptp_feature_certification_report cert_report = { + .report_id = ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTPHQA, + .ptphqa_blob = {0xfc, 0x28, 0xfe, 0x84, 0x40, 0xcb, 0x9a, 0x87, 0x0d, 0xbe, 0x57, 0x3c, 0xb6, + 0x70, 0x09, 0x88, 0x07, 0x97, 0x2d, 0x2b, 0xe3, 0x38, 0x34, 0xb6, 0x6c, 0xed, + 0xb0, 0xf7, 0xe5, 0x9c, 0xf6, 0xc2, 0x2e, 0x84, 0x1b, 0xe8, 0xb4, 0x51, 0x78, + 0x43, 0x1f, 0x28, 0x4b, 0x7c, 0x2d, 0x53, 0xaf, 0xfc, 0x47, 0x70, 0x1b, 0x59, + 0x6f, 0x74, 0x43, 0xc4, 0xf3, 0x47, 0x18, 0x53, 0x1a, 0xa2, 0xa1, 0x71, 0xc7, + 0x95, 0x0e, 0x31, 0x55, 0x21, 0xd3, 0xb5, 0x1e, 0xe9, 0x0c, 0xba, 0xec, 0xb8, + 0x89, 0x19, 0x3e, 0xb3, 0xaf, 0x75, 0x81, 0x9d, 0x53, 0xb9, 0x41, 0x57, 0xf4, + 0x6d, 0x39, 0x25, 0x29, 0x7c, 0x87, 0xd9, 0xb4, 0x98, 0x45, 0x7d, 0xa7, 0x26, + 0x9c, 0x65, 0x3b, 0x85, 0x68, 0x89, 0xd7, 0x3b, 0xbd, 0xff, 0x14, 0x67, 0xf2, + 0x2b, 0xf0, 0x2a, 0x41, 0x54, 0xf0, 0xfd, 0x2c, 0x66, 0x7c, 0xf8, 0xc0, 0x8f, + 0x33, 0x13, 0x03, 0xf1, 0xd3, 0xc1, 0x0b, 0x89, 0xd9, 0x1b, 0x62, 0xcd, 0x51, + 0xb7, 0x80, 0xb8, 0xaf, 0x3a, 0x10, 0xc1, 0x8a, 0x5b, 0xe8, 0x8a, 0x56, 0xf0, + 0x8c, 0xaa, 0xfa, 0x35, 0xe9, 0x42, 0xc4, 0xd8, 0x55, 0xc3, 0x38, 0xcc, 0x2b, + 0x53, 0x5c, 0x69, 0x52, 0xd5, 0xc8, 0x73, 0x02, 0x38, 0x7c, 0x73, 0xb6, 0x41, + 0xe7, 0xff, 0x05, 0xd8, 0x2b, 0x79, 0x9a, 0xe2, 0x34, 0x60, 0x8f, 0xa3, 0x32, + 0x1f, 0x09, 0x78, 0x62, 0xbc, 0x80, 0xe3, 0x0f, 0xbd, 0x65, 0x20, 0x08, 0x13, + 0xc1, 0xe2, 0xee, 0x53, 0x2d, 0x86, 0x7e, 0xa7, 0x5a, 0xc5, 0xd3, 0x7d, 0x98, + 0xbe, 0x31, 0x48, 0x1f, 0xfb, 0xda, 0xaf, 0xa2, 0xa8, 0x6a, 0x89, 0xd6, 0xbf, + 0xf2, 0xd3, 0x32, 0x2a, 0x9a, 0xe4, 0xcf, 0x17, 0xb7, 0xb8, 0xf4, 0xe1, 0x33, + 0x08, 0x24, 0x8b, 0xc4, 0x43, 0xa5, 0xe5, 0x24, 0xc2}, +}; + +struct zmk_hid_ptp_feature_certification_report * +zmk_mouse_hid_ptp_get_feature_certification_report() { + return &cert_report; +} + +static struct zmk_hid_ptp_feature_capabilities_report cap_report = { + .report_id = ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_CAPABILITIES, + .body = + { + .max_touches = CONFIG_ZMK_TRACKPAD_FINGERS, + .pad_type = PTP_PAD_TYPE_NON_CLICKABLE, + }, +}; + +struct zmk_hid_ptp_feature_capabilities_report * +zmk_mouse_hid_ptp_get_feature_capabilities_report() { + return &cap_report; +} + +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) \ No newline at end of file diff --git a/app/src/mouse/hog.c b/app/src/mouse/hog.c new file mode 100644 index 000000000000..79abe37b5cfc --- /dev/null +++ b/app/src/mouse/hog.c @@ -0,0 +1,352 @@ +/* + * 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 = 0x0000, + .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, +}; + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +// Windows PTP Collection + +static struct hids_report ptp_input = { + .id = ZMK_MOUSE_HID_REPORT_ID_DIGITIZER, + .type = HIDS_INPUT, +}; + +static struct hids_report ptp_caps = { + .id = ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_CAPABILITIES, + .type = HIDS_FEATURE, +}; + +static struct hids_report ptp_hqa = { + .id = ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTPHQA, + .type = HIDS_FEATURE, +}; + +// Configuration Collection + +static struct hids_report ptp_selective_reporting = { + .id = ZMK_MOUSE_HID_REPORT_ID_DIGITIZER_SEL_REPORTING, + .type = HIDS_FEATURE, +}; + +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +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)); +} + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +static ssize_t read_hids_ptp_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) { + struct zmk_hid_ptp_report_body *report_body = &zmk_mouse_hid_get_ptp_report()->body; + return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, + sizeof(struct zmk_hid_ptp_report_body)); +} + +static ssize_t read_hids_ptp_caps_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) { + struct zmk_hid_ptp_feature_capabilities_report_body *report_body = + &zmk_mouse_hid_ptp_get_feature_capabilities_report()->body; + return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, + sizeof(struct zmk_hid_ptp_feature_capabilities_report_body)); +} + +static ssize_t read_hids_ptp_hqa_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) { + struct zmk_hid_ptp_feature_certification *report = &zmk_hid_ptp_feature_certification_report(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, report->blob, sizeof(report->blob)); +} + +static ssize_t write_hids_ptp_sel_reporting(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags) { + if (offset != 0) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + if (len != sizeof(struct zmk_hid_ptp_feature_selective_report_body)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + struct zmk_hid_ptp_feature_selective_report_body *report = + (struct zmk_hid_ptp_feature_selective_report_body *)buf; + LOG_DBG("Selective: surface: %d, button: %d", report->surface_switch, report->button_switch); + // TODO: Do something with it! + + return len; +} + +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +static void input_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value) { + 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_PROTOCOL_MODE, BT_GATT_CHRC_WRITE_WITHOUT_RESP, + // BT_GATT_PERM_WRITE, NULL, write_proto_mode, &proto_mode), + 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_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), + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ_ENCRYPT, read_hids_ptp_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, &ptp_input), + + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ_ENCRYPT, read_hids_ptp_caps_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, &ptp_caps), + + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ_ENCRYPT, read_hids_ptp_hqa_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, &ptp_hqa), + + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, + BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_WRITE_ENCRYPT, NULL, write_hids_ptp_sel_reporting, 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, &ptp_selective_reporting), + +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + + 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(); + LOG_DBG("Address pointer %p", 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; +}; + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +K_MSGQ_DEFINE(zmk_hog_ptp_msgq, sizeof(struct zmk_hid_ptp_report_body), + CONFIG_ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE, 4); + +void send_ptp_report_callback(struct k_work *work) { + struct zmk_hid_ptp_report_body report; + while (k_msgq_get(&zmk_hog_ptp_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[9], + .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_ptp_work, send_ptp_report_callback); + +int zmk_mouse_hog_send_ptp_report(struct zmk_hid_ptp_report_body *report) { + int err = k_msgq_put(&zmk_hog_ptp_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_ptp_report_body discarded_report; + k_msgq_get(&zmk_hog_ptp_msgq, &discarded_report, K_NO_WAIT); + return zmk_hog_send_ptp_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_ptp_work); + + return 0; +}; +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +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 0acc434997f3..546e39063d82 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 enum input_listener_xy_data_mode { INPUT_LISTENER_XY_DATA_MODE_NONE, @@ -27,12 +31,33 @@ struct input_listener_xy_data { int16_t y; }; +struct input_listener_ptp_finger { + int16_t x; + int16_t y; + bool active; +}; + struct input_listener_data { - struct input_listener_xy_data data; - struct input_listener_xy_data wheel_data; + union { + struct { + struct input_listener_xy_data data; + struct input_listener_xy_data wheel_data; - uint8_t button_set; - uint8_t button_clear; + uint8_t button_set; + uint8_t button_clear; + } mouse; + + struct { + struct input_listener_xy_data data; + uint8_t finger_idx; + bool touched; + } ptp; + }; +}; + +enum input_listener_mode { + INPUT_LISTENER_MODE_MOUSE, + INPUT_LISTENER_MODE_PTP, }; struct input_listener_config { @@ -41,32 +66,55 @@ struct input_listener_config { bool y_invert; uint16_t scale_multiplier; uint16_t scale_divisor; + enum input_listener_mode mode; }; 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) { + if (config->mode == INPUT_LISTENER_MODE_PTP) { + switch (evt->code) { + case INPUT_ABS_MT_SLOT: + data->ptp.finger_idx = evt->value; + break; + case INPUT_ABS_X: + data->ptp.data.mode = INPUT_LISTENER_XY_DATA_MODE_ABS; + data->ptp.data.x = evt->value; + break; + case INPUT_ABS_Y: + data->ptp.data.mode = INPUT_LISTENER_XY_DATA_MODE_ABS; + data->ptp.data.y = evt->value; + break; + default: + break; + } + } +} + +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) { @@ -75,13 +123,18 @@ static void handle_key_code(struct input_listener_data *data, struct input_event case INPUT_BTN_2: case INPUT_BTN_3: case INPUT_BTN_4: + // TODO Handle PTP mode! 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; + case INPUT_BTN_TOUCH: + if (config->mode == INPUT_LISTENER_MODE_PTP) { + data->ptp.touched = evt->value > 0; + } default: break; } @@ -130,44 +183,81 @@ 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); - } + switch (config->mode) { + case INPUT_LISTENER_MODE_MOUSE: + 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) { - zmk_hid_mouse_button_press(i); + 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) { - zmk_hid_mouse_button_release(i); + 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); + } } } - } - zmk_endpoints_send_mouse_report(); - zmk_hid_mouse_scroll_set(0, 0); - zmk_hid_mouse_movement_set(0, 0); + zmk_endpoints_send_mouse_report(); + zmk_hid_mouse_scroll_set(0, 0); + zmk_hid_mouse_movement_set(0, 0); + + clear_xy_data(&data->mouse.data); + clear_xy_data(&data->mouse.wheel_data); + + data->mouse.button_set = data->mouse.button_clear = 0; - clear_xy_data(&data->data); - clear_xy_data(&data->wheel_data); + break; + case INPUT_LISTENER_MODE_PTP: +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + struct zmk_ptp_finger finger = {.contact_id = data->ptp.finger_idx}; + finger.x = data->ptp.data.x; + finger.y = data->ptp.data.y; + WRITE_BIT(finger.valid_tip, 0, 1); + WRITE_BIT(finger.valid_tip, 1, data->ptp.touched ? 1 : 0); - data->button_set = data->button_clear = 0; + zmk_mouse_hid_set_ptp_finger(finger); + zmk_mouse_hid_ptp_update_scan_time(); + + // TODO: Button Data + zmk_endpoints_send_ptp_report(); + + if (!data->ptp.touched) { + finger.x = finger.y = 0; + WRITE_BIT(finger.valid_tip, 0, 0); + + zmk_mouse_hid_set_ptp_finger(finger); + zmk_mouse_hid_ptp_update_scan_time(); + + // TODO: Button Data + zmk_endpoints_send_ptp_report(); + } +#else + LOG_WRN("PTP Mode not supported without CONFIG_ZMK_TRACKPAD=y"); +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + + break; + } } } @@ -178,6 +268,7 @@ static void input_handler(const struct input_listener_config *config, .y_invert = DT_INST_PROP(n, y_invert), \ .scale_multiplier = DT_INST_PROP(n, scale_multiplier), \ .scale_divisor = DT_INST_PROP(n, scale_divisor), \ + .mode = DT_INST_ENUM_IDX_OR(n, mode, 0), \ }; \ static struct input_listener_data data_##n = {}; \ void input_handler_##n(struct input_event *evt) { \ diff --git a/app/src/mouse/usb_hid.c b/app/src/mouse/usb_hid.c new file mode 100644 index 000000000000..db2b926f9dee --- /dev/null +++ b/app/src/mouse/usb_hid.c @@ -0,0 +1,221 @@ +/* + * 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; +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + case ZMK_MOUSE_HID_REPORT_ID_DIGITIZER: + struct zmk_hid_ptp_report *ptp_report = zmk_mouse_hid_get_ptp_report(); + LOG_WRN("Get PTP report"); + *data = (uint8_t *)ptp_report; + *len = sizeof(*ptp_report); + break; + case ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_CAPABILITIES: + LOG_WRN("Get CAPABILITIES"); + struct zmk_hid_ptp_feature_capabilities_report *cap_report = + zmk_mouse_hid_ptp_get_feature_capabilities_report(); + *data = (uint8_t *)cap_report; + *len = sizeof(*cap_report); + break; + case ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTPHQA: + LOG_WRN("Get HQA"); + struct zmk_hid_ptp_feature_certification_report *cert_report = + zmk_mouse_hid_ptp_get_feature_certification_report(); + *data = (uint8_t *)cert_report; + *len = sizeof(*cert_report); + break; +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + 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) { +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) + case ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_CAPABILITIES: + LOG_DBG("PTP Capabilities"); + // if (*len != sizeof(struct zmk_hid_led_report)) { + // LOG_ERR("LED set report is malformed: length=%d", *len); + // return -EINVAL; + // } else { + // struct zmk_hid_led_report *report = (struct zmk_hid_led_report *)*data; + // struct zmk_endpoint_instance endpoint = { + // .transport = ZMK_TRANSPORT_USB, + // }; + // zmk_hid_indicators_process_report(&report->body, endpoint); + // } + break; + case ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_MODE: + if (*len != sizeof(struct zmk_hid_ptp_feature_mode_report)) { + LOG_ERR("Mode set report is malformed: length=%d", *len); + return -EINVAL; + } else { + struct zmk_hid_ptp_feature_mode_report *report = + (struct zmk_hid_ptp_feature_mode_report *)*data; + LOG_DBG("PTP mode: %d", report->mode); + zmk_mouse_hid_ptp_set_feature_mode(report->mode); + } + break; + case ZMK_MOUSE_HID_REPORT_ID_FEATURE_PTP_SELECTIVE: + if (*len != sizeof(struct zmk_hid_ptp_feature_selective_report)) { + LOG_ERR("Mode set report is malformed: length=%d", *len); + return -EINVAL; + } else { + struct zmk_hid_ptp_feature_selective_report *report = + (struct zmk_hid_ptp_feature_selective_report *)*data; + LOG_DBG("PTP selective: surface: %d, button: %d", report->body.surface_switch, + report->body.button_switch); + // TODO: Do something with it! + } + break; +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + 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) + +#if IS_ENABLED(CONFIG_ZMK_TRACKPAD) +int zmk_mouse_usb_hid_send_ptp_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_ptp_report *report = zmk_mouse_hid_get_ptp_report(); + return zmk_mouse_usb_hid_send_report((uint8_t *)report, sizeof(*report)); +} +#endif // IS_ENABLED(CONFIG_ZMK_TRACKPAD) + +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) {