diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
index 27173a91cf1..3609bb8d704 100644
--- a/builddefs/common_features.mk
+++ b/builddefs/common_features.mk
@@ -103,11 +103,6 @@ endif
VALID_STENO_PROTOCOL_TYPES := geminipr txbolt all
STENO_PROTOCOL ?= all
-ifeq ($(strip $(PLOVER_HID_ENABLE)), yes)
- OPT_DEFS += -DPLOVER_HID_ENABLE
- SRC += $(QUANTUM_DIR)/process_keycode/process_plover_hid.c
-endif
-
ifeq ($(strip $(STENO_ENABLE)), yes)
ifeq ($(filter $(STENO_PROTOCOL),$(VALID_STENO_PROTOCOL_TYPES)),)
$(call CATASTROPHIC_ERROR,Invalid STENO_PROTOCOL,STENO_PROTOCOL="$(STENO_PROTOCOL)" is not a valid stenography protocol)
diff --git a/builddefs/generic_features.mk b/builddefs/generic_features.mk
index 6d394977bd0..92fecf030e7 100644
--- a/builddefs/generic_features.mk
+++ b/builddefs/generic_features.mk
@@ -45,6 +45,7 @@ GENERIC_FEATURES = \
MOUSEKEY \
MUSIC \
OS_DETECTION \
+ PLOVER_HID \
PROGRAMMABLE_BUTTON \
REPEAT_KEY \
SECURE \
diff --git a/docs/features/ps2_mouse.md b/docs/features/ps2_mouse.md
index 6ef7e467682..9f875d9b816 100644
--- a/docs/features/ps2_mouse.md
+++ b/docs/features/ps2_mouse.md
@@ -166,7 +166,7 @@ In your keyboard config.h:
The `PIO` subsystem is a Raspberry Pi RP2040 specific implementation, using the integrated PIO peripheral and is therefore only available on this MCU.
-There are strict requirements for pin ordering but any pair of GPIO pins can be used. The GPIO used for clock must be directly after data, see the included info.json snippet for an example of correct order.
+The GPIOs used for clock and data must be consecutive (in either order).
You may optionally switch the PIO peripheral used with the following define in config.h:
```c
diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c
index 1c61f196bde..3b18f4f4b8b 100644
--- a/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c
+++ b/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c
@@ -21,8 +21,16 @@
# endif
#endif
-#if PS2_DATA_PIN + 1 != PS2_CLOCK_PIN
-# error PS/2 clock pin must be data pin + 1!
+#if PS2_DATA_PIN + 1 == PS2_CLOCK_PIN
+# define PS2_FIRST_PIN PS2_DATA_PIN
+# define PS2_DATA_PINDIR_BIT 1
+# define PS2_CLOCK_PINDIR_BIT 2
+#elif PS2_DATA_PIN - 1 == PS2_CLOCK_PIN
+# define PS2_FIRST_PIN PS2_CLOCK_PIN
+# define PS2_DATA_PINDIR_BIT 2
+# define PS2_CLOCK_PINDIR_BIT 1
+#else
+# error PS/2 clock and data pin must be consecutive!
#endif
static inline void pio_serve_interrupt(void);
@@ -50,29 +58,29 @@ OSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) {
// clang-format off
static const uint16_t ps2_program_instructions[] = {
- // .wrap_target
- 0x00c7, // 0: jmp pin, 7
- 0xe02a, // 1: set x, 10
- 0x2021, // 2: wait 0 pin, 1
- 0x4001, // 3: in pins, 1
- 0x20a1, // 4: wait 1 pin, 1
- 0x0042, // 5: jmp x--, 2
- 0x0000, // 6: jmp 0
- 0x00e9, // 7: jmp !osre, 9
- 0x0000, // 8: jmp 0
- 0xff81, // 9: set pindirs, 1 [31]
- 0xe280, // 10: set pindirs, 0 [2]
- 0xe082, // 11: set pindirs, 2
- 0x2021, // 12: wait 0 pin, 1
- 0xe029, // 13: set x, 9
- 0x6081, // 14: out pindirs, 1
- 0x20a1, // 15: wait 1 pin, 1
- 0x2021, // 16: wait 0 pin, 1
- 0x004e, // 17: jmp x--, 14
- 0xe083, // 18: set pindirs, 3
- 0x2021, // 19: wait 0 pin, 1
- 0x20a1, // 20: wait 1 pin, 1
- // .wrap
+ // .wrap_target
+ 0x00c7, // 0: jmp pin, 7
+ 0xe02a, // 1: set x, 10
+ 0x2000 | PS2_CLOCK_PIN, // 2: wait 0 gpio, CLK
+ 0x4001, // 3: in pins, 1
+ 0x2080 | PS2_CLOCK_PIN, // 4: wait 1 gpio, CLK
+ 0x0042, // 5: jmp x--, 2
+ 0x0000, // 6: jmp 0
+ 0x00e9, // 7: jmp !osre, 9
+ 0x0000, // 8: jmp 0
+ 0xff80 | PS2_DATA_PINDIR_BIT, // 9: set pindirs, DATA [31]
+ 0xe280, // 10: set pindirs, 0 [2]
+ 0xe080 | PS2_CLOCK_PINDIR_BIT, // 11: set pindirs, CLK
+ 0x2000 | PS2_CLOCK_PIN, // 12: wait 0 gpio, CLK
+ 0xe029, // 13: set x, 9
+ 0x6081, // 14: out pindirs, 1
+ 0x2080 | PS2_CLOCK_PIN, // 15: wait 1 gpio, CLK
+ 0x2000 | PS2_CLOCK_PIN, // 16: wait 0 gpio, CLK
+ 0x004e, // 17: jmp x--, 14
+ 0xe083, // 18: set pindirs, 3
+ 0x2000 | PS2_CLOCK_PIN, // 19: wait 0 gpio, CLK
+ 0x2080 | PS2_CLOCK_PIN, // 20: wait 1 gpio, CLK
+ // .wrap
};
// clang-format on
@@ -133,9 +141,9 @@ void ps2_host_init(void) {
sm_config_set_wrap(&c, offset + PS2_WRAP_TARGET, offset + PS2_WRAP);
// Set pindirs to input (output enable is inverted below)
- pio_sm_set_consecutive_pindirs(pio, state_machine, PS2_DATA_PIN, 2, true);
+ pio_sm_set_consecutive_pindirs(pio, state_machine, PS2_FIRST_PIN, 2, true);
sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / (200.0f * KHZ));
- sm_config_set_set_pins(&c, PS2_DATA_PIN, 2);
+ sm_config_set_set_pins(&c, PS2_FIRST_PIN, 2);
sm_config_set_out_pins(&c, PS2_DATA_PIN, 1);
sm_config_set_out_shift(&c, true, true, 10);
sm_config_set_in_shift(&c, true, true, 11);
diff --git a/quantum/plover_hid.c b/quantum/plover_hid.c
new file mode 100644
index 00000000000..436d0bb828b
--- /dev/null
+++ b/quantum/plover_hid.c
@@ -0,0 +1,46 @@
+/* Copyright 2021 dnaq
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "plover_hid.h"
+#include "report.h"
+#include "host.h"
+#include "compiler_support.h"
+
+// The button index handed to plover_hid_update() is `keycode - QK_PLOVER_HID`, so the report's
+// data field must hold a bit for every keycode in the range or the bit-packing below overflows.
+STATIC_ASSERT(sizeof(((report_plover_hid_t *)0)->data) * CHAR_BIT >= (QK_PLOVER_HID_MAX - QK_PLOVER_HID + 1), "Plover HID report data too small for the QK_PLOVER_HID keycode range");
+
+static report_plover_hid_t plover_hid_report = {.report_id = REPORT_ID_PLOVER_HID};
+static bool plover_hid_report_updated = false;
+
+void plover_hid_update(uint8_t button, bool pressed) {
+ if (pressed) {
+ plover_hid_report.data[button / 8] |= (1 << (7 - (button % 8)));
+ } else {
+ plover_hid_report.data[button / 8] &= ~(1 << (7 - (button % 8)));
+ }
+ plover_hid_report_updated = true;
+}
+
+void plover_hid_task(void) {
+ if (!plover_hid_report_updated) {
+ return;
+ }
+ host_plover_hid_send(&plover_hid_report);
+ plover_hid_report_updated = false;
+}
diff --git a/quantum/plover_hid.h b/quantum/plover_hid.h
index 855ceb9a3c5..8deb4955a70 100644
--- a/quantum/plover_hid.h
+++ b/quantum/plover_hid.h
@@ -16,5 +16,8 @@
#pragma once
+#include
+#include
+
void plover_hid_update(uint8_t button, bool pressed);
void plover_hid_task(void);
diff --git a/tmk_core/protocol/chibios/usb_endpoints.c b/tmk_core/protocol/chibios/usb_endpoints.c
index d0c43e32d17..4a5ce565c8e 100644
--- a/tmk_core/protocol/chibios/usb_endpoints.c
+++ b/tmk_core/protocol/chibios/usb_endpoints.c
@@ -89,11 +89,7 @@ usb_endpoint_in_t usb_endpoints_in[USB_ENDPOINT_IN_COUNT] = {
#endif
#if defined(PLOVER_HID_ENABLE)
-# if defined(USB_ENDPOINTS_ARE_REORDERABLE)
- [USB_ENDPOINT_IN_PLOVER_HID] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, PLOVER_HID_EPSIZE, PLOVER_HID_IN_EPNUM, PLOVER_HID_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(PLOVER_HID_EPSIZE)),
-# else
[USB_ENDPOINT_IN_PLOVER_HID] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, PLOVER_HID_EPSIZE, PLOVER_HID_IN_EPNUM, PLOVER_HID_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(PLOVER_HID_EPSIZE)),
-# endif
#endif
#if defined(MIDI_ENABLE)
@@ -127,6 +123,10 @@ usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES] = {
[XAP_INTERFACE] = USB_ENDPOINT_IN_XAP,
#endif
+#if defined(PLOVER_HID_ENABLE)
+ [PLOVER_HID_INTERFACE] = USB_ENDPOINT_IN_PLOVER_HID,
+#endif
+
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
[MOUSE_INTERFACE] = USB_ENDPOINT_IN_MOUSE,
#endif
diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c
index b438fd72dc0..94bc32f1da8 100644
--- a/tmk_core/protocol/chibios/usb_main.c
+++ b/tmk_core/protocol/chibios/usb_main.c
@@ -493,6 +493,12 @@ void send_digitizer(report_digitizer_t *report) {
#endif
}
+void send_plover_hid(report_plover_hid_t *report) {
+#ifdef PLOVER_HID_ENABLE
+ send_report(USB_ENDPOINT_IN_PLOVER_HID, report, sizeof(report_plover_hid_t));
+#endif
+}
+
/* ---------------------------------------------------------
* Console functions
* ---------------------------------------------------------
@@ -586,28 +592,6 @@ void xap_task(void) {
}
#endif // XAP_ENABLE
-#ifdef PLOVER_HID_ENABLE
-static bool plover_hid_report_updated = false;
-static uint8_t plover_hid_current_report[PLOVER_HID_EPSIZE] = {0x50};
-
-void plover_hid_update(uint8_t button, bool pressed) {
- if (pressed) {
- plover_hid_current_report[1 + button / 8] |= (1 << (7 - (button % 8)));
- } else {
- plover_hid_current_report[1 + button / 8] &= ~(1 << (7 - (button % 8)));
- }
- plover_hid_report_updated = true;
-}
-
-void plover_hid_task(void) {
- if (!plover_hid_report_updated) {
- return;
- }
- send_report(USB_ENDPOINT_IN_PLOVER_HID, plover_hid_current_report, sizeof(plover_hid_current_report));
- plover_hid_report_updated = false;
-}
-#endif
-
#ifdef MIDI_ENABLE
void send_midi_packet(MIDI_EventPacket_t *event) {
diff --git a/tmk_core/protocol/host.c b/tmk_core/protocol/host.c
index f4fa9064e03..8d326026074 100644
--- a/tmk_core/protocol/host.c
+++ b/tmk_core/protocol/host.c
@@ -342,6 +342,14 @@ void host_programmable_button_send(uint32_t data) {
__attribute__((weak)) void send_programmable_button(report_programmable_button_t *report) {}
+#ifdef PLOVER_HID_ENABLE
+void host_plover_hid_send(report_plover_hid_t *report) {
+ send_plover_hid(report);
+}
+#endif
+
+__attribute__((weak)) void send_plover_hid(report_plover_hid_t *report) {}
+
#ifdef RAW_ENABLE
void host_raw_hid_send(uint8_t *data, uint8_t length) {
host_driver_t *driver = host_get_active_driver();
diff --git a/tmk_core/protocol/host.h b/tmk_core/protocol/host.h
index 7ff6d0df08d..c34a3f927c4 100644
--- a/tmk_core/protocol/host.h
+++ b/tmk_core/protocol/host.h
@@ -44,6 +44,7 @@ void host_mouse_send(report_mouse_t *report);
void host_system_send(uint16_t usage);
void host_consumer_send(uint16_t usage);
void host_programmable_button_send(uint32_t data);
+void host_plover_hid_send(report_plover_hid_t *report);
void host_raw_hid_send(uint8_t *data, uint8_t length);
uint16_t host_last_system_usage(void);
diff --git a/tmk_core/protocol/host_driver.h b/tmk_core/protocol/host_driver.h
index c2835aaa99f..f65c367e020 100644
--- a/tmk_core/protocol/host_driver.h
+++ b/tmk_core/protocol/host_driver.h
@@ -37,3 +37,4 @@ typedef struct {
void send_joystick(report_joystick_t *report);
void send_digitizer(report_digitizer_t *report);
void send_programmable_button(report_programmable_button_t *report);
+void send_plover_hid(report_plover_hid_t *report);
diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c
index 05d33b8f953..d697144eac7 100644
--- a/tmk_core/protocol/lufa/lufa.c
+++ b/tmk_core/protocol/lufa/lufa.c
@@ -68,10 +68,6 @@
# include "xap.h"
#endif
-#ifdef PLOVER_HID_ENABLE
-# include "plover_hid.h"
-#endif
-
#ifdef WAIT_FOR_USB
// TODO: Remove backwards compatibility with old define
# define USB_WAIT_FOR_ENUMERATION
@@ -286,28 +282,6 @@ void xap_task(void) {
}
#endif // XAP_ENABLE
-#ifdef PLOVER_HID_ENABLE
-static bool plover_hid_report_updated = false;
-static uint8_t plover_hid_current_report[PLOVER_HID_EPSIZE] = {0x50};
-
-void plover_hid_update(uint8_t button, bool pressed) {
- if (pressed) {
- plover_hid_current_report[1 + button / 8] |= (1 << (7 - (button % 8)));
- } else {
- plover_hid_current_report[1 + button / 8] &= ~(1 << (7 - (button % 8)));
- }
- plover_hid_report_updated = true;
-}
-
-void plover_hid_task(void) {
- if (!plover_hid_report_updated) {
- return;
- }
- send_report(PLOVER_HID_IN_EPNUM, plover_hid_current_report, sizeof(plover_hid_current_report));
- plover_hid_report_updated = false;
-}
-#endif
-
/*******************************************************************************
* Console
******************************************************************************/
@@ -711,6 +685,12 @@ void send_digitizer(report_digitizer_t *report) {
#endif
}
+void send_plover_hid(report_plover_hid_t *report) {
+#ifdef PLOVER_HID_ENABLE
+ send_report(PLOVER_HID_IN_EPNUM, report, sizeof(report_plover_hid_t));
+#endif
+}
+
/*******************************************************************************
* sendchar
******************************************************************************/
@@ -986,10 +966,6 @@ void protocol_post_task(void) {
MIDI_Device_USBTask(&USB_MIDI_Interface);
#endif
-#ifdef PLOVER_HID_ENABLE
- plover_hid_task();
-#endif
-
#ifdef VIRTSER_ENABLE
virtser_task();
CDC_Device_USBTask(&cdc_device);
diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h
index ec220c6938d..805f8a91745 100644
--- a/tmk_core/protocol/report.h
+++ b/tmk_core/protocol/report.h
@@ -44,6 +44,11 @@ enum hid_report_ids {
#define IS_VALID_REPORT_ID(id) ((id) >= REPORT_ID_ALL && (id) <= REPORT_ID_COUNT)
+// Plover HID has its own dedicated interface rather than the shared endpoint, so its report ID
+// is fixed by the protocol at 0x50 ('P') and is intentionally NOT a member of enum
+// hid_report_ids above (which only enumerates shared-endpoint reports up to REPORT_ID_COUNT).
+#define REPORT_ID_PLOVER_HID 0x50
+
/* Mouse buttons */
#define MOUSE_BTN_MASK(n) (1 << (n))
enum mouse_buttons {
@@ -193,6 +198,11 @@ typedef struct {
uint32_t usage;
} PACKED report_programmable_button_t;
+typedef struct {
+ uint8_t report_id; // REPORT_ID_PLOVER_HID
+ uint8_t data[8];
+} PACKED report_plover_hid_t;
+
#ifdef MOUSE_EXTENDED_REPORT
# define MOUSE_REPORT_XY_MIN (INT16_MIN + 1)
# define MOUSE_REPORT_XY_MAX INT16_MAX
diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c
index e88e2dc863e..b59958976c0 100644
--- a/tmk_core/protocol/usb_descriptor.c
+++ b/tmk_core/protocol/usb_descriptor.c
@@ -40,6 +40,7 @@
#include "report.h"
#include "usb_descriptor.h"
#include "usb_descriptor_common.h"
+#include "compiler_support.h"
#ifdef JOYSTICK_ENABLE
# include "joystick.h"
@@ -494,6 +495,10 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM PloverReport[] = {
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_END_COLLECTION(0),
};
+
+// The Plover HID report is sent with sizeof(report_plover_hid_t), but the endpoint and descriptor
+// are sized with PLOVER_HID_EPSIZE; they must match or reports get truncated/padded.
+STATIC_ASSERT(sizeof(report_plover_hid_t) == PLOVER_HID_EPSIZE, "report_plover_hid_t size must match PLOVER_HID_EPSIZE");
#endif
#ifdef CONSOLE_ENABLE