diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
index b7cb2193fa3..e65cbc0d071 100644
--- a/builddefs/common_features.mk
+++ b/builddefs/common_features.mk
@@ -98,6 +98,12 @@ 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/show_options.mk b/builddefs/show_options.mk
index 1c1a3ebf8e0..ab1208ae83f 100644
--- a/builddefs/show_options.mk
+++ b/builddefs/show_options.mk
@@ -63,6 +63,7 @@ OTHER_OPTION_NAMES = \
LCD_BACKLIGHT_ENABLE \
MACROS_ENABLED \
PS2_ENABLE \
+ PLOVER_HID_ENABLE \
PS2_MOUSE_ENABLE \
PS2_DRIVER \
RAW_ENABLE \
diff --git a/data/constants/keycodes/keycodes_0.0.9.hjson b/data/constants/keycodes/keycodes_0.0.9.hjson
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/data/constants/keycodes/keycodes_0.0.9_plover.hjson b/data/constants/keycodes/keycodes_0.0.9_plover.hjson
new file mode 100644
index 00000000000..9a0da9a5a43
--- /dev/null
+++ b/data/constants/keycodes/keycodes_0.0.9_plover.hjson
@@ -0,0 +1,265 @@
+{
+ "ranges": {
+ "0x5A40/0x003F": {
+ "define": "QK_PLOVER_HID"
+ }
+ },
+ "keycodes": {
+ "0x5A40": {
+ "group": "plover_hid",
+ "key": "PLV_S1"
+ },
+ "0x5A41": {
+ "group": "plover_hid",
+ "key": "PLV_TL"
+ },
+ "0x5A42": {
+ "group": "plover_hid",
+ "key": "PLV_KL"
+ },
+ "0x5A43": {
+ "group": "plover_hid",
+ "key": "PLV_PL"
+ },
+ "0x5A44": {
+ "group": "plover_hid",
+ "key": "PLV_WL"
+ },
+ "0x5A45": {
+ "group": "plover_hid",
+ "key": "PLV_HL"
+ },
+ "0x5A46": {
+ "group": "plover_hid",
+ "key": "PLV_RL"
+ },
+ "0x5A47": {
+ "group": "plover_hid",
+ "key": "PLV_A"
+ },
+ "0x5A48": {
+ "group": "plover_hid",
+ "key": "PLV_O"
+ },
+ "0x5A49": {
+ "group": "plover_hid",
+ "key": "PLV_ST1"
+ },
+ "0x5A4A": {
+ "group": "plover_hid",
+ "key": "PLV_E"
+ },
+ "0x5A4B": {
+ "group": "plover_hid",
+ "key": "PLV_U"
+ },
+ "0x5A4C": {
+ "group": "plover_hid",
+ "key": "PLV_FR"
+ },
+ "0x5A4D": {
+ "group": "plover_hid",
+ "key": "PLV_RR"
+ },
+ "0x5A4E": {
+ "group": "plover_hid",
+ "key": "PLV_PR"
+ },
+ "0x5A4F": {
+ "group": "plover_hid",
+ "key": "PLV_BR"
+ },
+ "0x5A50": {
+ "group": "plover_hid",
+ "key": "PLV_LR"
+ },
+ "0x5A51": {
+ "group": "plover_hid",
+ "key": "PLV_GR"
+ },
+ "0x5A52": {
+ "group": "plover_hid",
+ "key": "PLV_TR"
+ },
+ "0x5A53": {
+ "group": "plover_hid",
+ "key": "PLV_SR"
+ },
+ "0x5A54": {
+ "group": "plover_hid",
+ "key": "PLV_DR"
+ },
+ "0x5A55": {
+ "group": "plover_hid",
+ "key": "PLV_ZR"
+ },
+ "0x5A56": {
+ "group": "plover_hid",
+ "key": "PLV_N1"
+ },
+ "0x5A57": {
+ "group": "plover_hid",
+ "key": "PLV_S2"
+ },
+ "0x5A58": {
+ "group": "plover_hid",
+ "key": "PLV_ST2"
+ },
+ "0x5A59": {
+ "group": "plover_hid",
+ "key": "PLV_ST3"
+ },
+ "0x5A5A": {
+ "group": "plover_hid",
+ "key": "PLV_ST4"
+ },
+ "0x5A5B": {
+ "group": "plover_hid",
+ "key": "PLV_N2"
+ },
+ "0x5A5C": {
+ "group": "plover_hid",
+ "key": "PLV_N3"
+ },
+ "0x5A5D": {
+ "group": "plover_hid",
+ "key": "PLV_N4"
+ },
+ "0x5A5E": {
+ "group": "plover_hid",
+ "key": "PLV_N5"
+ },
+ "0x5A5F": {
+ "group": "plover_hid",
+ "key": "PLV_N6"
+ },
+ "0x5A60": {
+ "group": "plover_hid",
+ "key": "PLV_N7"
+ },
+ "0x5A61": {
+ "group": "plover_hid",
+ "key": "PLV_N8"
+ },
+ "0x5A62": {
+ "group": "plover_hid",
+ "key": "PLV_N9"
+ },
+ "0x5A63": {
+ "group": "plover_hid",
+ "key": "PLV_NA"
+ },
+ "0x5A64": {
+ "group": "plover_hid",
+ "key": "PLV_NB"
+ },
+ "0x5A65": {
+ "group": "plover_hid",
+ "key": "PLV_NC"
+ },
+ "0x5A66": {
+ "group": "plover_hid",
+ "key": "PLV_X1"
+ },
+ "0x5A67": {
+ "group": "plover_hid",
+ "key": "PLV_X2"
+ },
+ "0x5A68": {
+ "group": "plover_hid",
+ "key": "PLV_X3"
+ },
+ "0x5A69": {
+ "group": "plover_hid",
+ "key": "PLV_X4"
+ },
+ "0x5A6A": {
+ "group": "plover_hid",
+ "key": "PLV_X5"
+ },
+ "0x5A6B": {
+ "group": "plover_hid",
+ "key": "PLV_X6"
+ },
+ "0x5A6C": {
+ "group": "plover_hid",
+ "key": "PLV_X7"
+ },
+ "0x5A6D": {
+ "group": "plover_hid",
+ "key": "PLV_X8"
+ },
+ "0x5A6E": {
+ "group": "plover_hid",
+ "key": "PLV_X9"
+ },
+ "0x5A6F": {
+ "group": "plover_hid",
+ "key": "PLV_X10"
+ },
+ "0x5A70": {
+ "group": "plover_hid",
+ "key": "PLV_X11"
+ },
+ "0x5A71": {
+ "group": "plover_hid",
+ "key": "PLV_X12"
+ },
+ "0x5A72": {
+ "group": "plover_hid",
+ "key": "PLV_X13"
+ },
+ "0x5A73": {
+ "group": "plover_hid",
+ "key": "PLV_X14"
+ },
+ "0x5A74": {
+ "group": "plover_hid",
+ "key": "PLV_X15"
+ },
+ "0x5A75": {
+ "group": "plover_hid",
+ "key": "PLV_X16"
+ },
+ "0x5A76": {
+ "group": "plover_hid",
+ "key": "PLV_X17"
+ },
+ "0x5A77": {
+ "group": "plover_hid",
+ "key": "PLV_X18"
+ },
+ "0x5A78": {
+ "group": "plover_hid",
+ "key": "PLV_X19"
+ },
+ "0x5A79": {
+ "group": "plover_hid",
+ "key": "PLV_X20"
+ },
+ "0x5A7A": {
+ "group": "plover_hid",
+ "key": "PLV_X21"
+ },
+ "0x5A7B": {
+ "group": "plover_hid",
+ "key": "PLV_X22"
+ },
+ "0x5A7C": {
+ "group": "plover_hid",
+ "key": "PLV_X23"
+ },
+ "0x5A7D": {
+ "group": "plover_hid",
+ "key": "PLV_X24"
+ },
+ "0x5A7E": {
+ "group": "plover_hid",
+ "key": "PLV_X25"
+ },
+ "0x5A7F": {
+ "group": "plover_hid",
+ "key": "PLV_X26"
+ }
+ }
+}
diff --git a/docs/features/stenography.md b/docs/features/stenography.md
index b253890e134..ec0027310fd 100644
--- a/docs/features/stenography.md
+++ b/docs/features/stenography.md
@@ -6,11 +6,12 @@ The [Open Steno Project](https://www.openstenoproject.org/) has built an open-so
## Steno Support in QMK
-There are three ways that QMK keyboards can support steno, with varying degrees of configuration required:
+There are four ways that QMK keyboards can support steno, with varying degrees of configuration required:
1. Plover with [Arpeggiation](https://plover.wiki/index.php/Glossary#Arpeggiate) requires no changes to any keyboard and is supported by QMK as well as any other QWERTY keyboard.
2. Plover with [NKRO](https://plover.wiki/index.php/Using_a_standard_keyboard_with_Plover#NKRO). If your keyboard supports NKRO in hardware and you have NKRO enabled as a USB endpoint, you can chord with the keyboard. Many devices will arrive stock like this and will require no changes.
3. Steno Machine Protocols. This requires the most configuration, but this has the advantage of allowing you to use your keyboard keys normally (either on another layer or another piece of hardware) without enabling and disabling your steno software.
+4. Plover HID Protocol. This is a custom HID protocol that Plover can understand, but does not require a COM port. It is robust to device disconnects and saves an interface over the COM-based alternatives.
## Plover with QWERTY Keyboard {#plover-with-qwerty-keyboard}
@@ -92,6 +93,22 @@ Examples of steno strokes and the associated packet:
- `WAZ` = `10000000 00000010 00100000 00000000 00000000 00000001`
- `PHAPBGS` = `10000000 00000101 00100000 00000000 01101010 00000000`
+## Plover HID Protocol {#plover-hid-protocol}
+
+This mode operates independently of the other Steno "official" machine protocols. Like any other mouse or keyboard, it uses the USB HID protocol to communicate with Plover by sending an 8 byte (64 bit) packet representing a bitfield for all the possible keys on a steno machine plus a number of additional general purpose keys for custom use. This protocol is only understood by Plover as of [5.1.0](https://github.com/opensteno/plover/releases/tag/v5.1.0).
+
+Add
+
+```make
+PLOVER_HID_ENABLE = yes
+```
+
+to your `rules.mk` file. This does **not** require `STENO_ENABLE` to be set.
+
+All of the possible keycodes are defined with the `PLV_` prefix and are available without any additional includes.
+
+More details can be found here: https://github.com/dnaq/plover-machine-hid
+
### Switching protocols on the fly {#switching-protocols-on-the-fly}
If you wish to switch the serial protocol used to transfer the steno chords without having to recompile your keyboard firmware every time, you can press the `QK_STENO_BOLT` and `QK_STENO_GEMINI` keycodes in order to switch protocols on the fly.
diff --git a/quantum/keycodes.h b/quantum/keycodes.h
index 132eb79c6e1..d0127196d58 100644
--- a/quantum/keycodes.h
+++ b/quantum/keycodes.h
@@ -26,11 +26,11 @@
#pragma once
// clang-format off
-#define QMK_KEYCODES_VERSION "0.0.8"
-#define QMK_KEYCODES_VERSION_BCD 0x00000008
+#define QMK_KEYCODES_VERSION "0.0.9"
+#define QMK_KEYCODES_VERSION_BCD 0x00000009
#define QMK_KEYCODES_VERSION_MAJOR 0
#define QMK_KEYCODES_VERSION_MINOR 0
-#define QMK_KEYCODES_VERSION_PATCH 8
+#define QMK_KEYCODES_VERSION_PATCH 9
enum qk_keycode_ranges {
// Ranges
@@ -64,6 +64,8 @@ enum qk_keycode_ranges {
QK_SWAP_HANDS_MAX = 0x56FF,
QK_TAP_DANCE = 0x5700,
QK_TAP_DANCE_MAX = 0x57FF,
+ QK_PLOVER_HID = 0x5A40,
+ QK_PLOVER_HID_MAX = 0x5A7F,
QK_MAGIC = 0x7000,
QK_MAGIC_MAX = 0x70FF,
QK_MIDI = 0x7100,
@@ -329,6 +331,70 @@ enum qk_keycode_defines {
QK_SWAP_HANDS_OFF = 0x56F4,
QK_SWAP_HANDS_ON = 0x56F5,
QK_SWAP_HANDS_ONE_SHOT = 0x56F6,
+ PLV_S1 = 0x5A40,
+ PLV_TL = 0x5A41,
+ PLV_KL = 0x5A42,
+ PLV_PL = 0x5A43,
+ PLV_WL = 0x5A44,
+ PLV_HL = 0x5A45,
+ PLV_RL = 0x5A46,
+ PLV_A = 0x5A47,
+ PLV_O = 0x5A48,
+ PLV_ST1 = 0x5A49,
+ PLV_E = 0x5A4A,
+ PLV_U = 0x5A4B,
+ PLV_FR = 0x5A4C,
+ PLV_RR = 0x5A4D,
+ PLV_PR = 0x5A4E,
+ PLV_BR = 0x5A4F,
+ PLV_LR = 0x5A50,
+ PLV_GR = 0x5A51,
+ PLV_TR = 0x5A52,
+ PLV_SR = 0x5A53,
+ PLV_DR = 0x5A54,
+ PLV_ZR = 0x5A55,
+ PLV_N1 = 0x5A56,
+ PLV_S2 = 0x5A57,
+ PLV_ST2 = 0x5A58,
+ PLV_ST3 = 0x5A59,
+ PLV_ST4 = 0x5A5A,
+ PLV_N2 = 0x5A5B,
+ PLV_N3 = 0x5A5C,
+ PLV_N4 = 0x5A5D,
+ PLV_N5 = 0x5A5E,
+ PLV_N6 = 0x5A5F,
+ PLV_N7 = 0x5A60,
+ PLV_N8 = 0x5A61,
+ PLV_N9 = 0x5A62,
+ PLV_NA = 0x5A63,
+ PLV_NB = 0x5A64,
+ PLV_NC = 0x5A65,
+ PLV_X1 = 0x5A66,
+ PLV_X2 = 0x5A67,
+ PLV_X3 = 0x5A68,
+ PLV_X4 = 0x5A69,
+ PLV_X5 = 0x5A6A,
+ PLV_X6 = 0x5A6B,
+ PLV_X7 = 0x5A6C,
+ PLV_X8 = 0x5A6D,
+ PLV_X9 = 0x5A6E,
+ PLV_X10 = 0x5A6F,
+ PLV_X11 = 0x5A70,
+ PLV_X12 = 0x5A71,
+ PLV_X13 = 0x5A72,
+ PLV_X14 = 0x5A73,
+ PLV_X15 = 0x5A74,
+ PLV_X16 = 0x5A75,
+ PLV_X17 = 0x5A76,
+ PLV_X18 = 0x5A77,
+ PLV_X19 = 0x5A78,
+ PLV_X20 = 0x5A79,
+ PLV_X21 = 0x5A7A,
+ PLV_X22 = 0x5A7B,
+ PLV_X23 = 0x5A7C,
+ PLV_X24 = 0x5A7D,
+ PLV_X25 = 0x5A7E,
+ PLV_X26 = 0x5A7F,
QK_MAGIC_SWAP_CONTROL_CAPS_LOCK = 0x7000,
QK_MAGIC_UNSWAP_CONTROL_CAPS_LOCK = 0x7001,
QK_MAGIC_TOGGLE_CONTROL_CAPS_LOCK = 0x7002,
@@ -1483,6 +1549,7 @@ enum qk_keycode_defines {
#define IS_QK_PERSISTENT_DEF_LAYER(code) ((code) >= QK_PERSISTENT_DEF_LAYER && (code) <= QK_PERSISTENT_DEF_LAYER_MAX)
#define IS_QK_SWAP_HANDS(code) ((code) >= QK_SWAP_HANDS && (code) <= QK_SWAP_HANDS_MAX)
#define IS_QK_TAP_DANCE(code) ((code) >= QK_TAP_DANCE && (code) <= QK_TAP_DANCE_MAX)
+#define IS_QK_PLOVER_HID(code) ((code) >= QK_PLOVER_HID && (code) <= QK_PLOVER_HID_MAX)
#define IS_QK_MAGIC(code) ((code) >= QK_MAGIC && (code) <= QK_MAGIC_MAX)
#define IS_QK_MIDI(code) ((code) >= QK_MIDI && (code) <= QK_MIDI_MAX)
#define IS_QK_SEQUENCER(code) ((code) >= QK_SEQUENCER && (code) <= QK_SEQUENCER_MAX)
@@ -1509,6 +1576,7 @@ enum qk_keycode_defines {
#define IS_MOUSE_KEYCODE(code) ((code) >= QK_MOUSE_CURSOR_UP && (code) <= QK_MOUSE_ACCELERATION_2)
#define IS_MODIFIER_KEYCODE(code) ((code) >= KC_LEFT_CTRL && (code) <= KC_RIGHT_GUI)
#define IS_SWAP_HANDS_KEYCODE(code) ((code) >= QK_SWAP_HANDS_TOGGLE && (code) <= QK_SWAP_HANDS_ONE_SHOT)
+#define IS_PLOVER_HID_KEYCODE(code) ((code) >= PLV_S1 && (code) <= PLV_X26)
#define IS_MAGIC_KEYCODE(code) ((code) >= QK_MAGIC_SWAP_CONTROL_CAPS_LOCK && (code) <= QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK)
#define IS_MIDI_KEYCODE(code) ((code) >= QK_MIDI_ON && (code) <= QK_MIDI_PITCH_BEND_UP)
#define IS_SEQUENCER_KEYCODE(code) ((code) >= QK_SEQUENCER_ON && (code) <= QK_SEQUENCER_STEPS_CLEAR)
@@ -1535,6 +1603,7 @@ enum qk_keycode_defines {
#define MOUSE_KEYCODE_RANGE QK_MOUSE_CURSOR_UP ... QK_MOUSE_ACCELERATION_2
#define MODIFIER_KEYCODE_RANGE KC_LEFT_CTRL ... KC_RIGHT_GUI
#define SWAP_HANDS_KEYCODE_RANGE QK_SWAP_HANDS_TOGGLE ... QK_SWAP_HANDS_ONE_SHOT
+#define PLOVER_HID_KEYCODE_RANGE PLV_S1 ... PLV_X26
#define MAGIC_KEYCODE_RANGE QK_MAGIC_SWAP_CONTROL_CAPS_LOCK ... QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK
#define MIDI_KEYCODE_RANGE QK_MIDI_ON ... QK_MIDI_PITCH_BEND_UP
#define SEQUENCER_KEYCODE_RANGE QK_SEQUENCER_ON ... QK_SEQUENCER_STEPS_CLEAR
diff --git a/quantum/main.c b/quantum/main.c
index 3159c558507..6889ad3b346 100644
--- a/quantum/main.c
+++ b/quantum/main.c
@@ -55,6 +55,11 @@ int main(void) {
raw_hid_task();
#endif
+#ifdef PLOVER_HID_ENABLE
+ void plover_hid_task(void);
+ plover_hid_task();
+#endif
+
#ifdef CONSOLE_ENABLE
void console_task(void);
console_task();
diff --git a/quantum/plover_hid.h b/quantum/plover_hid.h
new file mode 100644
index 00000000000..855ceb9a3c5
--- /dev/null
+++ b/quantum/plover_hid.h
@@ -0,0 +1,20 @@
+/* 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 .
+ */
+
+#pragma once
+
+void plover_hid_update(uint8_t button, bool pressed);
+void plover_hid_task(void);
diff --git a/quantum/process_keycode/process_plover_hid.c b/quantum/process_keycode/process_plover_hid.c
new file mode 100644
index 00000000000..1c36ab46210
--- /dev/null
+++ b/quantum/process_keycode/process_plover_hid.c
@@ -0,0 +1,26 @@
+/* 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 "process_plover_hid.h"
+#include "plover_hid.h"
+
+bool process_plover_hid(uint16_t keycode, keyrecord_t *record) {
+ if (!IS_QK_PLOVER_HID(keycode)) {
+ return true;
+ }
+ plover_hid_update(keycode - QK_PLOVER_HID, record->event.pressed);
+ return false;
+}
diff --git a/quantum/process_keycode/process_plover_hid.h b/quantum/process_keycode/process_plover_hid.h
new file mode 100644
index 00000000000..40472c7fee8
--- /dev/null
+++ b/quantum/process_keycode/process_plover_hid.h
@@ -0,0 +1,21 @@
+/* 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 .
+ */
+
+#pragma once
+
+#include "quantum.h"
+
+bool process_plover_hid(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/quantum.c b/quantum/quantum.c
index be2e84f6d5e..196bb0534de 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -374,6 +374,9 @@ bool process_record_quantum(keyrecord_t *record) {
#if defined(LED_MATRIX_ENABLE)
process_led_matrix(keycode, record) &&
#endif
+#ifdef PLOVER_HID_ENABLE
+ process_plover_hid(keycode, record) &&
+#endif
#ifdef STENO_ENABLE
process_steno(keycode, record) &&
#endif
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 176c8a292d8..ede54a74f6d 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -95,6 +95,10 @@ extern layer_state_t layer_state;
# endif
#endif
+#ifdef PLOVER_HID_ENABLE
+# include "process_plover_hid.h"
+#endif
+
#ifdef STENO_ENABLE
# include "process_steno.h"
#endif
diff --git a/tmk_core/protocol/chibios/usb_endpoints.c b/tmk_core/protocol/chibios/usb_endpoints.c
index 00536845b23..2c30a4a54de 100644
--- a/tmk_core/protocol/chibios/usb_endpoints.c
+++ b/tmk_core/protocol/chibios/usb_endpoints.c
@@ -80,6 +80,14 @@ usb_endpoint_in_t usb_endpoints_in[USB_ENDPOINT_IN_COUNT] = {
# endif
#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)
# if defined(USB_ENDPOINTS_ARE_REORDERABLE)
[USB_ENDPOINT_IN_MIDI] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL),
diff --git a/tmk_core/protocol/chibios/usb_endpoints.h b/tmk_core/protocol/chibios/usb_endpoints.h
index 9fad83726e9..4662db393f0 100644
--- a/tmk_core/protocol/chibios/usb_endpoints.h
+++ b/tmk_core/protocol/chibios/usb_endpoints.h
@@ -43,6 +43,10 @@
# define RAW_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
+#if !defined(PLOVER_HID_IN_CAPACITY)
+# define PLOVER_HID_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
#if !defined(MIDI_STREAM_IN_CAPACITY)
# define MIDI_STREAM_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
@@ -90,6 +94,10 @@ typedef enum {
USB_ENDPOINT_IN_RAW,
#endif
+#if defined(PLOVER_HID_ENABLE)
+ USB_ENDPOINT_IN_PLOVER_HID,
+#endif
+
#if defined(MIDI_ENABLE)
USB_ENDPOINT_IN_MIDI,
#endif
@@ -127,6 +135,7 @@ typedef enum {
#if defined(RAW_ENABLE)
USB_ENDPOINT_OUT_RAW,
#endif
+
#if defined(MIDI_ENABLE)
USB_ENDPOINT_OUT_MIDI,
#endif
diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c
index b8497684d67..261d796d38e 100644
--- a/tmk_core/protocol/chibios/usb_main.c
+++ b/tmk_core/protocol/chibios/usb_main.c
@@ -523,6 +523,28 @@ void raw_hid_task(void) {
#endif
+#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/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c
index 3e442ba033a..544b90b31af 100644
--- a/tmk_core/protocol/lufa/lufa.c
+++ b/tmk_core/protocol/lufa/lufa.c
@@ -64,6 +64,10 @@
# include "raw_hid.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
@@ -179,6 +183,28 @@ void raw_hid_task(void) {
}
#endif
+#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
******************************************************************************/
@@ -347,6 +373,11 @@ void EVENT_USB_Device_ConfigurationChanged(void) {
ConfigSuccess &= Endpoint_ConfigureEndpoint((RAW_OUT_EPNUM | ENDPOINT_DIR_OUT), EP_TYPE_INTERRUPT, RAW_EPSIZE, 1);
#endif
+#ifdef PLOVER_HID_ENABLE
+ /* Setup plover HID endpoints */
+ ConfigSuccess &= Endpoint_ConfigureEndpoint((PLOVER_HID_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, PLOVER_HID_EPSIZE, 1);
+#endif
+
#ifdef CONSOLE_ENABLE
/* Setup console endpoint */
ConfigSuccess &= Endpoint_ConfigureEndpoint((CONSOLE_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, CONSOLE_EPSIZE, 1);
@@ -846,6 +877,10 @@ 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/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c
index 38abb3781cd..9d94fef9737 100644
--- a/tmk_core/protocol/usb_descriptor.c
+++ b/tmk_core/protocol/usb_descriptor.c
@@ -478,6 +478,24 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM RawReport[] = {
};
#endif
+#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_COLLECTION(8, 0x01), // Application
+ HID_RI_REPORT_ID(8, 80),
+ 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_USAGE_MINIMUM(8, 0),
+ HID_RI_USAGE_MAXIMUM(8, 63),
+ HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+ HID_RI_END_COLLECTION(0),
+};
+#endif
+
#ifdef CONSOLE_ENABLE
const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {
HID_RI_USAGE_PAGE(16, 0xFF31), // Vendor Defined (PJRC Teensy compatible)
@@ -643,6 +661,46 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
},
#endif
+#ifdef PLOVER_HID_ENABLE
+ /*
+ * Plover HID
+ */
+ .Plover_Interface = {
+ .Header = {
+ .Size = sizeof(USB_Descriptor_Interface_t),
+ .Type = DTYPE_Interface
+ },
+ .InterfaceNumber = PLOVER_HID_INTERFACE,
+ .AlternateSetting = 0x00,
+ .TotalEndpoints = 1,
+ .Class = HID_CSCP_HIDClass,
+ .SubClass = HID_CSCP_NonBootSubclass,
+ .Protocol = HID_CSCP_NonBootProtocol,
+ .InterfaceStrIndex = NO_DESCRIPTOR
+ },
+ .Plover_HID = {
+ .Header = {
+ .Size = sizeof(USB_HID_Descriptor_HID_t),
+ .Type = HID_DTYPE_HID
+ },
+ .HIDSpec = VERSION_BCD(1, 1, 1),
+ .CountryCode = 0x00,
+ .TotalReportDescriptors = 1,
+ .HIDReportType = HID_DTYPE_Report,
+ .HIDReportLength = sizeof(PloverReport)
+ },
+ .Plover_INEndpoint = {
+ .Header = {
+ .Size = sizeof(USB_Descriptor_Endpoint_t),
+ .Type = DTYPE_Endpoint
+ },
+ .EndpointAddress = (ENDPOINT_DIR_IN | PLOVER_HID_IN_EPNUM),
+ .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
+ .EndpointSize = PLOVER_HID_EPSIZE,
+ .PollingIntervalMS = 0x01
+ },
+#endif
+
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
/*
* Mouse
@@ -1285,6 +1343,14 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
break;
#endif
+#ifdef PLOVER_HID_ENABLE
+ case PLOVER_HID_INTERFACE:
+ Address = &ConfigurationDescriptor.Plover_HID;
+ Size = sizeof(USB_HID_Descriptor_HID_t);
+
+ break;
+#endif
+
#ifdef CONSOLE_ENABLE
case CONSOLE_INTERFACE:
Address = &ConfigurationDescriptor.Console_HID;
@@ -1342,6 +1408,14 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
break;
#endif
+#ifdef PLOVER_HID_ENABLE
+ case PLOVER_HID_INTERFACE:
+ Address = &PloverReport;
+ Size = sizeof(PloverReport);
+
+ break;
+#endif
+
#ifdef CONSOLE_ENABLE
case CONSOLE_INTERFACE:
Address = &ConsoleReport;
diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h
index 366a380fdde..1095192c72c 100644
--- a/tmk_core/protocol/usb_descriptor.h
+++ b/tmk_core/protocol/usb_descriptor.h
@@ -78,6 +78,13 @@ typedef struct {
USB_Descriptor_Endpoint_t Raw_OUTEndpoint;
#endif
+#ifdef PLOVER_HID_ENABLE
+ // Plover HID Interface
+ USB_Descriptor_Interface_t Plover_Interface;
+ USB_HID_Descriptor_HID_t Plover_HID;
+ USB_Descriptor_Endpoint_t Plover_INEndpoint;
+#endif
+
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
// Mouse HID Interface
USB_Descriptor_Interface_t Mouse_Interface;
@@ -164,6 +171,10 @@ enum usb_interfaces {
RAW_INTERFACE,
#endif
+#ifdef PLOVER_HID_ENABLE
+ PLOVER_HID_INTERFACE,
+#endif
+
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
MOUSE_INTERFACE,
#endif
@@ -227,6 +238,10 @@ enum usb_endpoints {
# endif
#endif
+#ifdef PLOVER_HID_ENABLE
+ PLOVER_HID_IN_EPNUM = NEXT_EPNUM,
+#endif
+
#ifdef SHARED_EP_ENABLE
SHARED_IN_EPNUM = NEXT_EPNUM,
#endif
@@ -287,6 +302,7 @@ enum usb_endpoints {
#define SHARED_EPSIZE 32
#define MOUSE_EPSIZE 16
#define RAW_EPSIZE 32
+#define PLOVER_HID_EPSIZE 9
#define CONSOLE_EPSIZE 32
#define MIDI_STREAM_EPSIZE 64
#define CDC_NOTIFICATION_EPSIZE 8