Skip to content

Commit

Permalink
Use a single endpoint for HID reports (#3951)
Browse files Browse the repository at this point in the history
* Unify multiple HID interfaces into one

This reduces the number of USB endpoints required, which frees them up
for other things.

NKRO and EXTRAKEY always use the shared endpoint.

By default, MOUSEKEY also uses it. This means it won't work as a Boot
Procotol mouse in some BIOSes, etc. If you really think your
keyboard needs to work as a mouse in your BIOS, set
MOUSE_SHARED_EP = no in your rules.mk.

By default, the core keyboard does not use the shared endpoint, as not
all BIOSes are standards compliant and that's one place you don't want
to find out your keyboard doesn't work.. If you are really confident,
you can set KEYBOARD_SHARED_EP = yes to use the shared endpoint here
too.

* unify endpoints: ChibiOS protocol implementation

* fixup: missing #ifdef EXTRAKEY_ENABLEs

broke build on AVR with EXTRAKEY disabled

* endpoints: restore error when too many endpoints required

* lufa: wait up to 10ms to send keyboard input

This avoids packets being dropped when two reports are sent in quick
succession (eg. releasing a dual role key).

* endpoints: fix compile on ARM_ATSAM

* endpoint: ARM_ATSAM fixes

No longer use wrong or unexpected endpoint IDs

* endpoints: accommodate VUSB protocol

V-USB has its own, understandably simple ideas about the report formats.
It already blasts the mouse and extrakeys through one endpoint with
report IDs. We just stay out of its way.

* endpoints: document new endpoint configuration options

* endpoints: respect keyboard_report->mods in NKRO

The caller(s) of host_keyboard_send expect to be able to just drop
modifiers in the mods field and not worry about whether NKRO is in use.
This is a good thing. So we just shift it over if needs be.

* endpoints: report.c: update for new keyboard_report format
  • Loading branch information
abrasive authored and drashna committed Nov 16, 2018
1 parent 46cf8cc commit 39bd760
Show file tree
Hide file tree
Showing 11 changed files with 426 additions and 380 deletions.
29 changes: 29 additions & 0 deletions docs/config_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,32 @@ Use these to enable or disable building certain features. The more you have enab
* Forces the keyboard to wait for a USB connection to be established before it starts up
* `NO_USB_STARTUP_CHECK`
* Disables usb suspend check after keyboard startup. Usually the keyboard waits for the host to wake it up before any tasks are performed. This is useful for split keyboards as one half will not get a wakeup call but must send commands to the master.

## USB Endpoint Limitations

In order to provide services over USB, QMK has to use USB endpoints.
These are a finite resource: each microcontroller has only a certain number.
This limits what features can be enabled together.
If the available endpoints are exceeded, a build error is thrown.

The following features can require separate endpoints:

* `MOUSEKEY_ENABLE`
* `EXTRAKEY_ENABLE`
* `CONSOLE_ENABLE`
* `NKRO_ENABLE`
* `MIDI_ENABLE`
* `RAW_ENABLE`
* `VIRTSER_ENABLE`

In order to improve utilisation of the endpoints, the HID features can be combined to use a single endpoint.
By default, `MOUSEKEY`, `EXTRAKEY`, and `NKRO` are combined into a single endpoint.

The base keyboard functionality can also be combined into the endpoint,
by setting `KEYBOARD_SHARED_EP = yes`.
This frees up one more endpoint,
but it can prevent the keyboard working in some BIOSes,
as they do not implement Boot Keyboard protocol switching.

Combining the mouse also breaks Boot Mouse compatibility.
The mouse can be uncombined by setting `MOUSE_SHARED_EP = no` if this functionality is required.
21 changes: 21 additions & 0 deletions tmk_core/common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,31 @@ else
TMK_COMMON_SRC += $(COMMON_DIR)/magic.c
endif

SHARED_EP_ENABLE = no
MOUSE_SHARED_EP ?= yes
ifeq ($(strip $(KEYBOARD_SHARED_EP)), yes)
TMK_COMMON_DEFS += -DKEYBOARD_SHARED_EP
SHARED_EP_ENABLE = yes
# With the current usb_descriptor.c code,
# you can't share kbd without sharing mouse;
# that would be a very unexpected use case anyway
MOUSE_SHARED_EP = yes
endif

ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
TMK_COMMON_SRC += $(COMMON_DIR)/mousekey.c
TMK_COMMON_DEFS += -DMOUSEKEY_ENABLE
TMK_COMMON_DEFS += -DMOUSE_ENABLE

ifeq ($(strip $(MOUSE_SHARED_EP)), yes)
TMK_COMMON_DEFS += -DMOUSE_SHARED_EP
SHARED_EP_ENABLE = yes
endif
endif

ifeq ($(strip $(EXTRAKEY_ENABLE)), yes)
TMK_COMMON_DEFS += -DEXTRAKEY_ENABLE
SHARED_EP_ENABLE = yes
endif

ifeq ($(strip $(RAW_ENABLE)), yes)
Expand All @@ -111,6 +127,7 @@ endif

ifeq ($(strip $(NKRO_ENABLE)), yes)
TMK_COMMON_DEFS += -DNKRO_ENABLE
SHARED_EP_ENABLE = yes
endif

ifeq ($(strip $(USB_6KRO_ENABLE)), yes)
Expand Down Expand Up @@ -182,6 +199,10 @@ ifeq ($(strip $(KEYMAP_SECTION_ENABLE)), yes)
endif
endif

ifeq ($(strip $(SHARED_EP_ENABLE)), yes)
TMK_COMMON_DEFS += -DSHARED_EP_ENABLE
endif

# Bootloader address
ifdef STM32_BOOTLOADER_ADDRESS
TMK_COMMON_DEFS += -DSTM32_BOOTLOADER_ADDRESS=$(STM32_BOOTLOADER_ADDRESS)
Expand Down
22 changes: 22 additions & 0 deletions tmk_core/common/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "util.h"
#include "debug.h"

#ifdef NKRO_ENABLE
#include "keycode_config.h"
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;
Expand All @@ -46,6 +51,20 @@ uint8_t host_keyboard_leds(void)
void host_keyboard_send(report_keyboard_t *report)
{
if (!driver) return;
#if defined(NKRO_ENABLE) && defined(NKRO_SHARED_EP)
if (keyboard_protocol && keymap_config.nkro) {
/* The callers of this function assume that report->mods is where mods go in.
* But report->nkro.mods can be at a different offset if core keyboard does not have a report ID.
*/
report->nkro.mods = report->mods;
report->nkro.report_id = REPORT_ID_NKRO;
} else
#endif
{
#ifdef KEYBOARD_SHARED_EP
report->report_id = REPORT_ID_KEYBOARD;
#endif
}
(*driver->send_keyboard)(report);

if (debug_keyboard) {
Expand All @@ -60,6 +79,9 @@ void host_keyboard_send(report_keyboard_t *report)
void host_mouse_send(report_mouse_t *report)
{
if (!driver) return;
#ifdef MOUSE_SHARED_EP
report->report_id = REPORT_ID_MOUSE;
#endif
(*driver->send_mouse)(report);
}

Expand Down
21 changes: 17 additions & 4 deletions tmk_core/common/report.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "keycode_config.h"
#include "debug.h"
#include "util.h"
#include <string.h>

/** \brief has_anykey
*
Expand All @@ -27,8 +28,16 @@
uint8_t has_anykey(report_keyboard_t* keyboard_report)
{
uint8_t cnt = 0;
for (uint8_t i = 1; i < KEYBOARD_REPORT_SIZE; i++) {
if (keyboard_report->raw[i])
uint8_t *p = keyboard_report->keys;
uint8_t lp = sizeof(keyboard_report->keys);
#ifdef NKRO_ENABLE
if (keyboard_protocol && keymap_config.nkro) {
p = keyboard_report->nkro.bits;
lp = sizeof(keyboard_report->nkro.bits);
}
#endif
while (lp--) {
if (*p++)
cnt++;
}
return cnt;
Expand Down Expand Up @@ -237,7 +246,11 @@ void del_key_from_report(report_keyboard_t* keyboard_report, uint8_t key)
void clear_keys_from_report(report_keyboard_t* keyboard_report)
{
// not clear mods
for (int8_t i = 1; i < KEYBOARD_REPORT_SIZE; i++) {
keyboard_report->raw[i] = 0;
#ifdef NKRO_ENABLE
if (keyboard_protocol && keymap_config.nkro) {
memset(keyboard_report->nkro.bits, 0, sizeof(keyboard_report->nkro.bits));
return;
}
#endif
memset(keyboard_report->keys, 0, sizeof(keyboard_report->keys));
}
46 changes: 30 additions & 16 deletions tmk_core/common/report.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.


/* report id */
#define REPORT_ID_MOUSE 1
#define REPORT_ID_SYSTEM 2
#define REPORT_ID_CONSUMER 3
#define REPORT_ID_KEYBOARD 1
#define REPORT_ID_MOUSE 2
#define REPORT_ID_SYSTEM 3
#define REPORT_ID_CONSUMER 4
#define REPORT_ID_NKRO 5

/* mouse buttons */
#define MOUSE_BTN1 (1<<0)
Expand Down Expand Up @@ -72,32 +74,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define SYSTEM_WAKE_UP 0x0083


#define NKRO_SHARED_EP
/* key report size(NKRO or boot mode) */
#if defined(NKRO_ENABLE)
#if defined(PROTOCOL_PJRC)
#include "usb.h"
#define KEYBOARD_REPORT_SIZE KBD2_SIZE
#define KEYBOARD_REPORT_KEYS (KBD2_SIZE - 2)
#define KEYBOARD_REPORT_BITS (KBD2_SIZE - 1)
#elif defined(PROTOCOL_LUFA) || defined(PROTOCOL_CHIBIOS)
#if defined(PROTOCOL_LUFA) || defined(PROTOCOL_CHIBIOS)
#include "protocol/usb_descriptor.h"
#define KEYBOARD_REPORT_SIZE NKRO_EPSIZE
#define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2)
#define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1)
#define KEYBOARD_REPORT_BITS (SHARED_EPSIZE - 2)
#elif defined(PROTOCOL_ARM_ATSAM)
#include "protocol/arm_atsam/usb/udi_device_epsize.h"
#define KEYBOARD_REPORT_SIZE NKRO_EPSIZE
#define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2)
#define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1)
#undef NKRO_SHARED_EP
#undef MOUSE_SHARED_EP
#else
#error "NKRO not supported with this protocol"
#endif
#endif

#ifdef KEYBOARD_SHARED_EP
# define KEYBOARD_REPORT_SIZE 9
#else
# define KEYBOARD_REPORT_SIZE 8
# define KEYBOARD_REPORT_KEYS 6
#endif

#define KEYBOARD_REPORT_KEYS 6

/* VUSB hardcodes keyboard and mouse+extrakey only */
#if defined(PROTOCOL_VUSB)
#undef KEYBOARD_SHARED_EP
#undef MOUSE_SHARED_EP
#endif

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -126,19 +131,28 @@ extern "C" {
typedef union {
uint8_t raw[KEYBOARD_REPORT_SIZE];
struct {
#ifdef KEYBOARD_SHARED_EP
uint8_t report_id;
#endif
uint8_t mods;
uint8_t reserved;
uint8_t keys[KEYBOARD_REPORT_KEYS];
};
#ifdef NKRO_ENABLE
struct {
struct nkro_report {
#ifdef NKRO_SHARED_EP
uint8_t report_id;
#endif
uint8_t mods;
uint8_t bits[KEYBOARD_REPORT_BITS];
} nkro;
#endif
} __attribute__ ((packed)) report_keyboard_t;

typedef struct {
#ifdef MOUSE_SHARED_EP
uint8_t report_id;
#endif
uint8_t buttons;
int8_t x;
int8_t y;
Expand Down
5 changes: 3 additions & 2 deletions tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "udi_hid.h"
#include "udi_hid_kbd.h"
#include <string.h>
#include "report.h"

//***************************************************************************
// KBD
Expand Down Expand Up @@ -430,7 +431,7 @@ UDC_DESC_STORAGE udi_hid_exk_report_desc_t udi_hid_exk_report_desc = {
0x05, 0x01, // Usage Page (Generic Desktop),
0x09, 0x80, // Usage (System Control),
0xA1, 0x01, // Collection (Application),
0x85, 0x02, // Report ID (2) (System),
0x85, REPORT_ID_SYSTEM, // Report ID (2) (System),
0x16, 0x01, 0x00, // Logical Minimum (1),
0x26, 0x03, 0x00, // Logical Maximum (3),
0x1A, 0x81, 0x00, // Usage Minimum (81) (System Power Down),
Expand All @@ -445,7 +446,7 @@ UDC_DESC_STORAGE udi_hid_exk_report_desc_t udi_hid_exk_report_desc = {
0x05, 0x0C, // Usage Page (Consumer),
0x09, 0x01, // Usage (Consumer Control),
0xA1, 0x01, // Collection (Application),
0x85, 0x03, // Report ID (3) (Consumer),
0x85, REPORT_ID_CONSUMER, // Report ID (3) (Consumer),
0x16, 0x01, 0x00, // Logical Minimum (1),
0x26, 0x9C, 0x02, // Logical Maximum (668),
0x1A, 0x01, 0x00, // Usage Minimum (1),
Expand Down
Loading

0 comments on commit 39bd760

Please sign in to comment.