Преглед изворни кода

Add Support for USB programmable buttons (#12950)

Thomas Weißschuh пре 3 година
родитељ
комит
83988597f4

+ 6 - 0
common_features.mk

@@ -127,6 +127,12 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
     SRC += $(QUANTUM_DIR)/pointing_device.c
 endif
 
+ifeq ($(strip $(PROGRAMMABLE_BUTTON_ENABLE)), yes)
+    OPT_DEFS += -DPROGRAMMABLE_BUTTON_ENABLE
+    SRC += $(QUANTUM_DIR)/programmable_button.c
+    SRC += $(QUANTUM_DIR)/process_keycode/process_programmable_button.c
+endif
+
 VALID_EEPROM_DRIVER_TYPES := vendor custom transient i2c spi
 EEPROM_DRIVER ?= vendor
 ifeq ($(filter $(EEPROM_DRIVER),$(VALID_EEPROM_DRIVER_TYPES)),)

+ 1 - 0
docs/_summary.md

@@ -72,6 +72,7 @@
     * [Mod-Tap](mod_tap.md)
     * [Macros](feature_macros.md)
     * [Mouse Keys](feature_mouse_keys.md)
+    * [Programmable Button](feature_programmable_button.md)
     * [Space Cadet Shift](feature_space_cadet.md)
     * [US ANSI Shifted Keys](keycodes_us_ansi_shifted.md)
 

+ 74 - 0
docs/feature_programmable_button.md

@@ -0,0 +1,74 @@
+## Programmable Button
+
+Programmable button is a feature that can be used to send keys that have no
+predefined meaning.
+This means they can be processed on the host side by custom software without
+colliding without the operating system trying to interpret these keys.
+
+The keycodes are emitted according to the HID usage
+"Telephony Device Page" (0x0B), "Programmable button usage" (0x07).
+On Linux (> 5.14) they are handled automatically and translated to `KEY_MACRO#`
+keycodes.
+(Up to `KEY_MACRO30`)
+
+### Enabling Programmable Button support
+
+To enable Programmable Button, add the following line to your keymap’s `rules.mk`:
+
+```c
+PROGRAMMABLE_BUTTON_ENABLE = yes
+```
+
+### Mapping
+
+In your keymap you can use the following keycodes to map key presses to Programmable Buttons:
+
+|Key                     |Description                                                     |
+|------------------------|----------------------|
+|`PROGRAMMABLE_BUTTON_1` |Programmable button 1 |
+|`PROGRAMMABLE_BUTTON_2` |Programmable button 2 |
+|`PROGRAMMABLE_BUTTON_3` |Programmable button 3 |
+|`PROGRAMMABLE_BUTTON_4` |Programmable button 4 |
+|`PROGRAMMABLE_BUTTON_5` |Programmable button 5 |
+|`PROGRAMMABLE_BUTTON_6` |Programmable button 6 |
+|`PROGRAMMABLE_BUTTON_7` |Programmable button 7 |
+|`PROGRAMMABLE_BUTTON_8` |Programmable button 8 |
+|`PROGRAMMABLE_BUTTON_9` |Programmable button 9 |
+|`PROGRAMMABLE_BUTTON_10`|Programmable button 10|
+|`PROGRAMMABLE_BUTTON_11`|Programmable button 11|
+|`PROGRAMMABLE_BUTTON_12`|Programmable button 12|
+|`PROGRAMMABLE_BUTTON_13`|Programmable button 13|
+|`PROGRAMMABLE_BUTTON_14`|Programmable button 14|
+|`PROGRAMMABLE_BUTTON_15`|Programmable button 15|
+|`PROGRAMMABLE_BUTTON_16`|Programmable button 16|
+|`PROGRAMMABLE_BUTTON_17`|Programmable button 17|
+|`PROGRAMMABLE_BUTTON_18`|Programmable button 18|
+|`PROGRAMMABLE_BUTTON_19`|Programmable button 19|
+|`PROGRAMMABLE_BUTTON_20`|Programmable button 20|
+|`PROGRAMMABLE_BUTTON_21`|Programmable button 21|
+|`PROGRAMMABLE_BUTTON_22`|Programmable button 22|
+|`PROGRAMMABLE_BUTTON_23`|Programmable button 23|
+|`PROGRAMMABLE_BUTTON_24`|Programmable button 24|
+|`PROGRAMMABLE_BUTTON_25`|Programmable button 25|
+|`PROGRAMMABLE_BUTTON_26`|Programmable button 26|
+|`PROGRAMMABLE_BUTTON_27`|Programmable button 27|
+|`PROGRAMMABLE_BUTTON_28`|Programmable button 28|
+|`PROGRAMMABLE_BUTTON_29`|Programmable button 29|
+|`PROGRAMMABLE_BUTTON_30`|Programmable button 30|
+|`PROGRAMMABLE_BUTTON_31`|Programmable button 31|
+|`PROGRAMMABLE_BUTTON_32`|Programmable button 32|
+|`PB_1` to `PB_32`       |Aliases for keymaps   |
+
+### API
+
+You can also use a dedicated API defined in `programmable_button.h` to interact with this feature:
+
+```
+void programmable_button_clear(void);
+void programmable_button_send(void);
+void programmable_button_on(uint8_t code);
+void programmable_button_off(uint8_t code);
+bool programmable_button_is_on(uint8_t code);
+uint32_t programmable_button_get_report(void);
+void programmable_button_set_report(uint32_t report);
+```

+ 40 - 0
docs/keycodes.md

@@ -677,6 +677,46 @@ See also: [One Shot Keys](one_shot_keys.md)
 |`OS_OFF`    |Turns One Shot keys off           |
 |`OS_TOGG`   |Toggles One Shot keys status      |
 
+## Programmable Button Support :id=programmable-button
+
+See also: [Programmable Button](feature_programmable_button.md)
+
+|Key                     |Description                                                     |
+|------------------------|----------------------|
+|`PROGRAMMABLE_BUTTON_1` |Programmable button 1 |
+|`PROGRAMMABLE_BUTTON_2` |Programmable button 2 |
+|`PROGRAMMABLE_BUTTON_3` |Programmable button 3 |
+|`PROGRAMMABLE_BUTTON_4` |Programmable button 4 |
+|`PROGRAMMABLE_BUTTON_5` |Programmable button 5 |
+|`PROGRAMMABLE_BUTTON_6` |Programmable button 6 |
+|`PROGRAMMABLE_BUTTON_7` |Programmable button 7 |
+|`PROGRAMMABLE_BUTTON_8` |Programmable button 8 |
+|`PROGRAMMABLE_BUTTON_9` |Programmable button 9 |
+|`PROGRAMMABLE_BUTTON_10`|Programmable button 10|
+|`PROGRAMMABLE_BUTTON_11`|Programmable button 11|
+|`PROGRAMMABLE_BUTTON_12`|Programmable button 12|
+|`PROGRAMMABLE_BUTTON_13`|Programmable button 13|
+|`PROGRAMMABLE_BUTTON_14`|Programmable button 14|
+|`PROGRAMMABLE_BUTTON_15`|Programmable button 15|
+|`PROGRAMMABLE_BUTTON_16`|Programmable button 16|
+|`PROGRAMMABLE_BUTTON_17`|Programmable button 17|
+|`PROGRAMMABLE_BUTTON_18`|Programmable button 18|
+|`PROGRAMMABLE_BUTTON_19`|Programmable button 19|
+|`PROGRAMMABLE_BUTTON_20`|Programmable button 20|
+|`PROGRAMMABLE_BUTTON_21`|Programmable button 21|
+|`PROGRAMMABLE_BUTTON_22`|Programmable button 22|
+|`PROGRAMMABLE_BUTTON_23`|Programmable button 23|
+|`PROGRAMMABLE_BUTTON_24`|Programmable button 24|
+|`PROGRAMMABLE_BUTTON_25`|Programmable button 25|
+|`PROGRAMMABLE_BUTTON_26`|Programmable button 26|
+|`PROGRAMMABLE_BUTTON_27`|Programmable button 27|
+|`PROGRAMMABLE_BUTTON_28`|Programmable button 28|
+|`PROGRAMMABLE_BUTTON_29`|Programmable button 29|
+|`PROGRAMMABLE_BUTTON_30`|Programmable button 30|
+|`PROGRAMMABLE_BUTTON_31`|Programmable button 31|
+|`PROGRAMMABLE_BUTTON_32`|Programmable button 32|
+|`PB_1` to `PB_32`       |Aliases for keymaps   |
+
 ## Space Cadet :id=space-cadet
 
 See also: [Space Cadet](feature_space_cadet.md)

+ 5 - 0
quantum/action.c

@@ -18,6 +18,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "keycode.h"
 #include "keyboard.h"
 #include "mousekey.h"
+#include "programmable_button.h"
 #include "command.h"
 #include "led.h"
 #include "action_layer.h"
@@ -988,6 +989,10 @@ void clear_keyboard_but_mods_and_keys() {
     mousekey_clear();
     mousekey_send();
 #endif
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+    programmable_button_clear();
+    programmable_button_send();
+#endif
 }
 
 /** \brief Utilities for actions. (FIXME: Needs better description)

+ 7 - 0
quantum/keyboard.c

@@ -76,6 +76,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #ifdef JOYSTICK_ENABLE
 #    include "process_joystick.h"
 #endif
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+#    include "programmable_button.h"
+#endif
 #ifdef HD44780_ENABLE
 #    include "hd44780.h"
 #endif
@@ -542,6 +545,10 @@ MATRIX_LOOP_END:
     digitizer_task();
 #endif
 
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+    programmable_button_send();
+#endif
+
     // update LED
     if (led_status != host_keyboard_leds()) {
         led_status = host_keyboard_leds();

+ 31 - 0
quantum/process_keycode/process_programmable_button.c

@@ -0,0 +1,31 @@
+/*
+Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "process_programmable_button.h"
+#include "programmable_button.h"
+
+bool process_programmable_button(uint16_t keycode, keyrecord_t *record) {
+    if (keycode >= PROGRAMMABLE_BUTTON_MIN && keycode <= PROGRAMMABLE_BUTTON_MAX) {
+        uint8_t button = keycode - PROGRAMMABLE_BUTTON_MIN + 1;
+        if (record->event.pressed) {
+            programmable_button_on(button);
+        } else {
+            programmable_button_off(button);
+        }
+    }
+    return true;
+}

+ 23 - 0
quantum/process_keycode/process_programmable_button.h

@@ -0,0 +1,23 @@
+/*
+Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include "quantum.h"
+
+bool process_programmable_button(uint16_t keycode, keyrecord_t *record);

+ 37 - 0
quantum/programmable_button.c

@@ -0,0 +1,37 @@
+/*
+Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "programmable_button.h"
+#include "host.h"
+
+#define REPORT_BIT(index) (((uint32_t)1) << (index - 1))
+
+static uint32_t programmable_button_report = 0;
+
+void programmable_button_clear(void) { programmable_button_report = 0; }
+
+void programmable_button_send(void) { host_programmable_button_send(programmable_button_report); }
+
+void programmable_button_on(uint8_t index) { programmable_button_report |= REPORT_BIT(index); }
+
+void programmable_button_off(uint8_t index) { programmable_button_report &= ~REPORT_BIT(index); }
+
+bool programmable_button_is_on(uint8_t index) { return !!(programmable_button_report & REPORT_BIT(index)); };
+
+uint32_t programmable_button_get_report(void) { return programmable_button_report; };
+
+void programmable_button_set_report(uint32_t report) { programmable_button_report = report; }

+ 30 - 0
quantum/programmable_button.h

@@ -0,0 +1,30 @@
+/*
+Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "report.h"
+
+void     programmable_button_clear(void);
+void     programmable_button_send(void);
+void     programmable_button_on(uint8_t index);
+void     programmable_button_off(uint8_t index);
+bool     programmable_button_is_on(uint8_t index);
+uint32_t programmable_button_get_report(void);
+void     programmable_button_set_report(uint32_t report);

+ 3 - 0
quantum/quantum.c

@@ -295,6 +295,9 @@ bool process_record_quantum(keyrecord_t *record) {
 #endif
 #ifdef JOYSTICK_ENABLE
             process_joystick(keycode, record) &&
+#endif
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+            process_programmable_button(keycode, record) &&
 #endif
             true)) {
         return false;

+ 4 - 0
quantum/quantum.h

@@ -147,6 +147,10 @@ extern layer_state_t layer_state;
 #    include "process_joystick.h"
 #endif
 
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+#    include "process_programmable_button.h"
+#endif
+
 #ifdef GRAVE_ESC_ENABLE
 #    include "process_grave_esc.h"
 #endif

+ 70 - 0
quantum/quantum_keycodes.h

@@ -524,6 +524,40 @@ enum quantum_keycodes {
     // Additional magic key
     MAGIC_TOGGLE_GUI,
 
+    // Programmable Button
+    PROGRAMMABLE_BUTTON_1,
+    PROGRAMMABLE_BUTTON_2,
+    PROGRAMMABLE_BUTTON_3,
+    PROGRAMMABLE_BUTTON_4,
+    PROGRAMMABLE_BUTTON_5,
+    PROGRAMMABLE_BUTTON_6,
+    PROGRAMMABLE_BUTTON_7,
+    PROGRAMMABLE_BUTTON_8,
+    PROGRAMMABLE_BUTTON_9,
+    PROGRAMMABLE_BUTTON_10,
+    PROGRAMMABLE_BUTTON_11,
+    PROGRAMMABLE_BUTTON_12,
+    PROGRAMMABLE_BUTTON_13,
+    PROGRAMMABLE_BUTTON_14,
+    PROGRAMMABLE_BUTTON_15,
+    PROGRAMMABLE_BUTTON_16,
+    PROGRAMMABLE_BUTTON_17,
+    PROGRAMMABLE_BUTTON_18,
+    PROGRAMMABLE_BUTTON_19,
+    PROGRAMMABLE_BUTTON_20,
+    PROGRAMMABLE_BUTTON_21,
+    PROGRAMMABLE_BUTTON_22,
+    PROGRAMMABLE_BUTTON_23,
+    PROGRAMMABLE_BUTTON_24,
+    PROGRAMMABLE_BUTTON_25,
+    PROGRAMMABLE_BUTTON_26,
+    PROGRAMMABLE_BUTTON_27,
+    PROGRAMMABLE_BUTTON_28,
+    PROGRAMMABLE_BUTTON_29,
+    PROGRAMMABLE_BUTTON_30,
+    PROGRAMMABLE_BUTTON_31,
+    PROGRAMMABLE_BUTTON_32,
+
     // Start of custom keycode range for keyboards and keymaps - always leave at the end
     SAFE_RANGE
 };
@@ -854,3 +888,39 @@ enum quantum_keycodes {
 #define OS_TOGG ONESHOT_TOGGLE
 #define OS_ON ONESHOT_ENABLE
 #define OS_OFF ONESHOT_DISABLE
+
+// Programmable Button aliases
+#define PB_1 PROGRAMMABLE_BUTTON_1
+#define PB_2 PROGRAMMABLE_BUTTON_2
+#define PB_3 PROGRAMMABLE_BUTTON_3
+#define PB_4 PROGRAMMABLE_BUTTON_4
+#define PB_5 PROGRAMMABLE_BUTTON_5
+#define PB_6 PROGRAMMABLE_BUTTON_6
+#define PB_7 PROGRAMMABLE_BUTTON_7
+#define PB_8 PROGRAMMABLE_BUTTON_8
+#define PB_9 PROGRAMMABLE_BUTTON_9
+#define PB_10 PROGRAMMABLE_BUTTON_10
+#define PB_11 PROGRAMMABLE_BUTTON_11
+#define PB_12 PROGRAMMABLE_BUTTON_12
+#define PB_13 PROGRAMMABLE_BUTTON_13
+#define PB_14 PROGRAMMABLE_BUTTON_14
+#define PB_15 PROGRAMMABLE_BUTTON_15
+#define PB_16 PROGRAMMABLE_BUTTON_16
+#define PB_17 PROGRAMMABLE_BUTTON_17
+#define PB_18 PROGRAMMABLE_BUTTON_18
+#define PB_19 PROGRAMMABLE_BUTTON_19
+#define PB_20 PROGRAMMABLE_BUTTON_20
+#define PB_21 PROGRAMMABLE_BUTTON_21
+#define PB_22 PROGRAMMABLE_BUTTON_22
+#define PB_23 PROGRAMMABLE_BUTTON_23
+#define PB_24 PROGRAMMABLE_BUTTON_24
+#define PB_25 PROGRAMMABLE_BUTTON_25
+#define PB_26 PROGRAMMABLE_BUTTON_26
+#define PB_27 PROGRAMMABLE_BUTTON_27
+#define PB_28 PROGRAMMABLE_BUTTON_28
+#define PB_29 PROGRAMMABLE_BUTTON_29
+#define PB_30 PROGRAMMABLE_BUTTON_30
+#define PB_31 PROGRAMMABLE_BUTTON_31
+#define PB_32 PROGRAMMABLE_BUTTON_32
+#define PROGRAMMABLE_BUTTON_MIN PROGRAMMABLE_BUTTON_1
+#define PROGRAMMABLE_BUTTON_MAX PROGRAMMABLE_BUTTON_32

+ 2 - 1
show_options.mk

@@ -83,7 +83,8 @@ OTHER_OPTION_NAMES = \
   RGB_MATRIX_KEYPRESSES \
   LED_MIRRORED \
   RGBLIGHT_FULL_POWER \
-  LTO_ENABLE
+  LTO_ENABLE \
+  PROGRAMMABLE_BUTTON_ENABLE
 
 define NAME_ECHO
        @printf "  %-30s = %-16s # %s\\n" "$1" "$($1)" "$(origin $1)"

+ 4 - 0
tmk_core/common/chibios/suspend.c

@@ -7,6 +7,7 @@
 #include "action.h"
 #include "action_util.h"
 #include "mousekey.h"
+#include "programmable_button.h"
 #include "host.h"
 #include "suspend.h"
 #include "led.h"
@@ -79,6 +80,9 @@ void suspend_wakeup_init(void) {
 #ifdef MOUSEKEY_ENABLE
     mousekey_clear();
 #endif /* MOUSEKEY_ENABLE */
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+    programmable_button_clear();
+#endif /* PROGRAMMABLE_BUTTON_ENABLE */
 #ifdef EXTRAKEY_ENABLE
     host_system_send(0);
     host_consumer_send(0);

+ 13 - 2
tmk_core/common/host.c

@@ -30,8 +30,9 @@ extern keymap_config_t keymap_config;
 #endif
 
 static host_driver_t *driver;
-static uint16_t       last_system_report   = 0;
-static uint16_t       last_consumer_report = 0;
+static uint16_t       last_system_report              = 0;
+static uint16_t       last_consumer_report            = 0;
+static uint32_t       last_programmable_button_report = 0;
 
 void host_set_driver(host_driver_t *d) { driver = d; }
 
@@ -122,6 +123,16 @@ void host_digitizer_send(digitizer_t *digitizer) {
 
 __attribute__((weak)) void send_digitizer(report_digitizer_t *report) {}
 
+void host_programmable_button_send(uint32_t report) {
+    if (report == last_programmable_button_report) return;
+    last_programmable_button_report = report;
+
+    if (!driver) return;
+    (*driver->send_programmable_button)(report);
+}
+
 uint16_t host_last_system_report(void) { return last_system_report; }
 
 uint16_t host_last_consumer_report(void) { return last_consumer_report; }
+
+uint32_t host_last_programmable_button_report(void) { return last_programmable_button_report; }

+ 2 - 0
tmk_core/common/host.h

@@ -47,9 +47,11 @@ void    host_keyboard_send(report_keyboard_t *report);
 void    host_mouse_send(report_mouse_t *report);
 void    host_system_send(uint16_t data);
 void    host_consumer_send(uint16_t data);
+void    host_programmable_button_send(uint32_t data);
 
 uint16_t host_last_system_report(void);
 uint16_t host_last_consumer_report(void);
+uint32_t host_last_programmable_button_report(void);
 
 #ifdef __cplusplus
 }

+ 1 - 0
tmk_core/common/host_driver.h

@@ -29,6 +29,7 @@ typedef struct {
     void (*send_mouse)(report_mouse_t *);
     void (*send_system)(uint16_t);
     void (*send_consumer)(uint16_t);
+    void (*send_programmable_button)(uint32_t);
 } host_driver_t;
 
 void send_digitizer(report_digitizer_t *report);

+ 6 - 0
tmk_core/common/report.h

@@ -29,6 +29,7 @@ enum hid_report_ids {
     REPORT_ID_MOUSE,
     REPORT_ID_SYSTEM,
     REPORT_ID_CONSUMER,
+    REPORT_ID_PROGRAMMABLE_BUTTON,
     REPORT_ID_NKRO,
     REPORT_ID_JOYSTICK,
     REPORT_ID_DIGITIZER
@@ -195,6 +196,11 @@ typedef struct {
     uint16_t usage;
 } __attribute__((packed)) report_extra_t;
 
+typedef struct {
+    uint8_t  report_id;
+    uint32_t usage;
+} __attribute__((packed)) report_programmable_button_t;
+
 typedef struct {
 #ifdef MOUSE_SHARED_EP
     uint8_t report_id;

+ 23 - 10
tmk_core/protocol/lufa/lufa.c

@@ -142,7 +142,8 @@ static void    send_keyboard(report_keyboard_t *report);
 static void    send_mouse(report_mouse_t *report);
 static void    send_system(uint16_t data);
 static void    send_consumer(uint16_t data);
-host_driver_t  lufa_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
+static void    send_programmable_button(uint32_t data);
+host_driver_t  lufa_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer, send_programmable_button};
 
 #ifdef VIRTSER_ENABLE
 // clang-format off
@@ -760,27 +761,31 @@ static void send_mouse(report_mouse_t *report) {
 #endif
 }
 
-/** \brief Send Extra
- *
- * FIXME: Needs doc
- */
-#ifdef EXTRAKEY_ENABLE
-static void send_extra(uint8_t report_id, uint16_t data) {
+static void send_report(void *report, size_t size) {
     uint8_t timeout = 255;
 
     if (USB_DeviceState != DEVICE_STATE_Configured) return;
 
-    static report_extra_t r;
-    r = (report_extra_t){.report_id = report_id, .usage = data};
     Endpoint_SelectEndpoint(SHARED_IN_EPNUM);
 
     /* Check if write ready for a polling interval around 10ms */
     while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
     if (!Endpoint_IsReadWriteAllowed()) return;
 
-    Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
+    Endpoint_Write_Stream_LE(report, size, NULL);
     Endpoint_ClearIN();
 }
+
+/** \brief Send Extra
+ *
+ * FIXME: Needs doc
+ */
+#ifdef EXTRAKEY_ENABLE
+static void send_extra(uint8_t report_id, uint16_t data) {
+    static report_extra_t r;
+    r = (report_extra_t){.report_id = report_id, .usage = data};
+    send_report(&r, sizeof(r));
+}
 #endif
 
 /** \brief Send System
@@ -822,6 +827,14 @@ static void send_consumer(uint16_t data) {
 #endif
 }
 
+static void send_programmable_button(uint32_t data) {
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+    static report_programmable_button_t r;
+    r = (report_programmable_button_t){.report_id = REPORT_ID_PROGRAMMABLE_BUTTON, .usage = data};
+    send_report(&r, sizeof(r));
+#endif
+}
+
 /*******************************************************************************
  * sendchar
  ******************************************************************************/

+ 19 - 0
tmk_core/protocol/usb_descriptor.c

@@ -237,6 +237,25 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
     HID_RI_END_COLLECTION(0),
 #endif
 
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+    HID_RI_USAGE_PAGE(8, 0x0C),            // Consumer
+    HID_RI_USAGE(8, 0x01),                 // Consumer Control
+    HID_RI_COLLECTION(8, 0x01),            // Application
+        HID_RI_REPORT_ID(8, REPORT_ID_PROGRAMMABLE_BUTTON),
+        HID_RI_USAGE(8, 0x09),             // Programmable Buttons
+        HID_RI_COLLECTION(8, 0x04),        // Named Array
+            HID_RI_USAGE_PAGE(8, 0x09),    // Button
+            HID_RI_USAGE_MINIMUM(8, 0x01), // Button 1
+            HID_RI_USAGE_MAXIMUM(8, 0x20), // Button 32
+            HID_RI_LOGICAL_MINIMUM(8, 0x01),
+            HID_RI_LOGICAL_MAXIMUM(8, 0x01),
+            HID_RI_REPORT_COUNT(8, 32),
+            HID_RI_REPORT_SIZE(8, 1),
+            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+        HID_RI_END_COLLECTION(0),
+    HID_RI_END_COLLECTION(0),
+#endif
+
 #ifdef NKRO_ENABLE
     HID_RI_USAGE_PAGE(8, 0x01),        // Generic Desktop
     HID_RI_USAGE(8, 0x06),             // Keyboard

+ 35 - 1
tmk_core/protocol/vusb/vusb.c

@@ -226,8 +226,9 @@ static void    send_keyboard(report_keyboard_t *report);
 static void    send_mouse(report_mouse_t *report);
 static void    send_system(uint16_t data);
 static void    send_consumer(uint16_t data);
+static void    send_programmable_button(uint32_t data);
 
-static host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
+static host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer, send_programmable_button};
 
 host_driver_t *vusb_driver(void) { return &driver; }
 
@@ -296,6 +297,19 @@ void send_digitizer(report_digitizer_t *report) {
 #ifdef DIGITIZER_ENABLE
     if (usbInterruptIsReadyShared()) {
         usbSetInterruptShared((void *)report, sizeof(report_digitizer_t));
+#endif
+}
+
+static void send_programmable_button(uint32_t data) {
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+    static report_programmable_button_t report = {
+        .report_id = REPORT_ID_PROGRAMMABLE_BUTTON,
+    };
+
+    report.usage = data;
+
+    if (usbInterruptIsReadyShared()) {
+        usbSetInterruptShared((void *)&report, sizeof(report));
     }
 #endif
 }
@@ -558,6 +572,26 @@ const PROGMEM uchar shared_hid_report[] = {
     0xC0               // End Collection
 #endif
 
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+    // Programmable buttons report descriptor
+    0x05, 0x0C,                           // Usage Page (Consumer)
+    0x09, 0x01,                           // Usage (Consumer Control)
+    0xA1, 0x01,                           // Collection (Application)
+    0x85, REPORT_ID_PROGRAMMABLE_BUTTON,  //   Report ID
+    0x09, 0x03,                           //   Usage (Programmable Buttons)
+    0xA1, 0x04,                           //   Collection (Named Array)
+    0x05, 0x09,                           //     Usage Page (Button)
+    0x19, 0x01,                           //     Usage Minimum (Button 1)
+    0x29, 0x20,                           //     Usage Maximum (Button 32)
+    0x15, 0x00,                           //     Logical Minimum (0)
+    0x25, 0x01,                           //     Logical Maximum (1)
+    0x95, 0x20,                           //     Report Count (32)
+    0x75, 0x01,                           //     Report Size (1)
+    0x81, 0x02,                           //     Input (Data, Variable, Absolute)
+    0xC0,                                 //   End Collection
+    0xC0                                  // End Collection
+#endif
+
 #ifdef SHARED_EP_ENABLE
 };
 #endif