From d36397fdd4d94261a82e74ce3d54d6e074c05499 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Thu, 18 Jun 2026 21:55:21 +0100 Subject: [PATCH] Implement Plover HID for VUSB (#26267) --- tmk_core/protocol/usb_descriptor.c | 12 ++--- tmk_core/protocol/vusb/vusb.c | 83 ++++++++++++++++++++++++++++++ tmk_core/protocol/vusb/vusb.h | 6 +++ 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index ff85e82f5c1..53316d56ed1 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -481,17 +481,17 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM RawReport[] = { #ifdef PLOVER_HID_ENABLE const USB_Descriptor_HIDReport_Datatype_t PROGMEM PloverReport[] = { - HID_RI_USAGE_PAGE(16, 0xff50), // - HID_RI_USAGE(16, 0x4c56), // Vendor Defined (0xff P L V) + HID_RI_USAGE_PAGE(16, 0xFF50), // + HID_RI_USAGE(16, 0x4C56), // Vendor Defined (0xff P L V) HID_RI_COLLECTION(8, 0x01), // Application - HID_RI_REPORT_ID(8, 80), + HID_RI_REPORT_ID(8, REPORT_ID_PLOVER_HID), HID_RI_LOGICAL_MINIMUM(8, 0), HID_RI_LOGICAL_MAXIMUM(8, 1), HID_RI_REPORT_SIZE(8, 1), - HID_RI_REPORT_COUNT(8, 64), - HID_RI_USAGE_PAGE(8, 0x0a), // Usage Page: Ordinal + HID_RI_REPORT_COUNT(8, 0x40), + HID_RI_USAGE_PAGE(8, 0x0A), // Usage Page: Ordinal HID_RI_USAGE_MINIMUM(8, 0), - HID_RI_USAGE_MAXIMUM(8, 63), + HID_RI_USAGE_MAXIMUM(8, 0x3F), HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_RI_END_COLLECTION(0), }; diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index e2e28a2b105..727b8799dcc 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -68,6 +68,10 @@ enum usb_interfaces { RAW_INTERFACE, #endif +#ifdef PLOVER_HID_ENABLE + PLOVER_HID_INTERFACE, +#endif + #if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) SHARED_INTERFACE, #endif @@ -87,6 +91,10 @@ STATIC_ASSERT(TOTAL_INTERFACES <= MAX_INTERFACES, "There are not enough availabl # error Mouse/Extra Keys share an endpoint with Console. Please disable one of the two. #endif +#if defined(PLOVER_HID_ENABLE) && defined(RAW_ENABLE) +# error Plover HID shares an endpoint with Raw HID. Please disable one of the two. +#endif + static report_keyboard_t keyboard_report_sent; static void send_report_fragment(uint8_t endpoint, void *data, size_t size) { @@ -281,6 +289,14 @@ void send_programmable_button(report_programmable_button_t *report) { #endif } +#define PLOVER_HID_EPSIZE 9 + +void send_plover_hid(report_plover_hid_t *report) { +#ifdef PLOVER_HID_ENABLE + send_report(4, report, sizeof(report_plover_hid_t)); +#endif +} + /*------------------------------------------------------------------* * Request from host * *------------------------------------------------------------------*/ @@ -771,6 +787,26 @@ const PROGMEM uchar raw_hid_report[] = { }; #endif +#ifdef PLOVER_HID_ENABLE +// clang-format off +const PROGMEM uchar plover_hid_report[] = { + 0x06, 0x50, 0xFF, // Usage Page (Vendor Defined) + 0x0A, 0x56, 0x4C, // Usage (Vendor Defined) (0xff P L V) + 0xA1, 0x01, // Collection (Application) + 0x85, REPORT_ID_PLOVER_HID, // Report ID + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x40, // Report Count (43) + 0x05, 0x0A, // Usage Page: Ordinal + 0x19, 0x00, // Usage Minimum + 0x29, 0x3F, // Usage Maximum (63) + 0x81, 0x02, // Output (Data, Variable, Absolute) + 0xC0 // End Collection +}; +// clang-format on +#endif + #if defined(CONSOLE_ENABLE) const PROGMEM uchar console_hid_report[] = { 0x06, 0x31, 0xFF, // Usage Page (Vendor Defined - PJRC Teensy compatible) @@ -963,6 +999,46 @@ const PROGMEM usbConfigurationDescriptor_t usbConfigurationDescriptor = { }, # endif +# if defined(PLOVER_HID_ENABLE) + /* + * Plover HID + */ + .ploverInterface = { + .header = { + .bLength = sizeof(usbInterfaceDescriptor_t), + .bDescriptorType = USBDESCR_INTERFACE + }, + .bInterfaceNumber = PLOVER_HID_INTERFACE, + .bAlternateSetting = 0x00, + .bNumEndpoints = 1, + .bInterfaceClass = 0x03, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00 + }, + .ploverHID = { + .header = { + .bLength = sizeof(usbHIDDescriptor_t), + .bDescriptorType = USBDESCR_HID + }, + .bcdHID = 0x0101, + .bCountryCode = 0x00, + .bNumDescriptors = 1, + .bDescriptorType = USBDESCR_HID_REPORT, + .wDescriptorLength = sizeof(plover_hid_report) + }, + .ploverINEndpoint = { + .header = { + .bLength = sizeof(usbEndpointDescriptor_t), + .bDescriptorType = USBDESCR_ENDPOINT + }, + .bEndpointAddress = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP4_NUMBER), + .bmAttributes = 0x03, + .wMaxPacketSize = PLOVER_HID_EPSIZE, + .bInterval = 0x01 + }, +# endif + # ifdef SHARED_EP_ENABLE /* * Shared @@ -1140,6 +1216,13 @@ USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) { break; #endif +#if defined(PLOVER_HID_ENABLE) + case PLOVER_HID_INTERFACE: + usbMsgPtr = (usbMsgPtr_t)plover_hid_report; + len = sizeof(plover_hid_report); + break; +#endif + #ifdef SHARED_EP_ENABLE case SHARED_INTERFACE: usbMsgPtr = (usbMsgPtr_t)shared_hid_report; diff --git a/tmk_core/protocol/vusb/vusb.h b/tmk_core/protocol/vusb/vusb.h index 4750e95bf25..16a6b397cc4 100644 --- a/tmk_core/protocol/vusb/vusb.h +++ b/tmk_core/protocol/vusb/vusb.h @@ -104,6 +104,12 @@ typedef struct usbConfigurationDescriptor { usbEndpointDescriptor_t rawOUTEndpoint; #endif +#if defined(PLOVER_HID_ENABLE) + usbInterfaceDescriptor_t ploverInterface; + usbHIDDescriptor_t ploverHID; + usbEndpointDescriptor_t ploverINEndpoint; +#endif + #if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) usbInterfaceDescriptor_t sharedInterface; usbHIDDescriptor_t sharedHID;