Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement NKRO for VUSB #12010

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion keyboards/spiderisland/split78/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ BOOTMAGIC_ENABLE = lite # Virtual DIP switch configuration
MOUSEKEY_ENABLE = no # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
COMMAND_ENABLE = no # Commands for debug and configuration
COMMAND_ENABLE = yes # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
NKRO_ENABLE = yes
Comment on lines +14 to +19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roll these back, optionally putting them into a personal keymap instead.
No need to change these defaults.

WS2812_DRIVER = i2c

# custom matrix setup
Expand Down
4 changes: 1 addition & 3 deletions tmk_core/common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ else
endif

ifeq ($(strip $(NKRO_ENABLE)), yes)
ifeq ($(PROTOCOL), VUSB)
$(info NKRO is not currently supported on V-USB, and has been disabled.)
else ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
$(info NKRO is not currently supported with Bluetooth, and has been disabled.)
else ifneq ($(BLUETOOTH),)
$(info NKRO is not currently supported with Bluetooth, and has been disabled.)
Expand Down
3 changes: 3 additions & 0 deletions tmk_core/common/report.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ enum desktop_usages {
# define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1)
# undef NKRO_SHARED_EP
# undef MOUSE_SHARED_EP
# elif defined(PROTOCOL_VUSB)
# define KEYBOARD_REPORT_BITS 15
# undef NKRO_SHARED_EP
# else
# error "NKRO not supported with this protocol"
# endif
Expand Down
139 changes: 135 additions & 4 deletions tmk_core/protocol/vusb/vusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debug.h"
#include "wait.h"
#include "usb_descriptor_common.h"
#include "keycode_config.h"

#ifdef RAW_ENABLE
# include "raw_hid.h"
Expand Down Expand Up @@ -60,6 +61,12 @@ enum usb_interfaces {
RAW_INTERFACE = NEXT_INTERFACE,
#endif

// Not part of SHARED to save on the report ID byte and fit into
// just two 8-bit interrupt transfers
#ifdef NKRO_ENABLE
NKRO_INTERFACE = NEXT_INTERFACE,
#endif

#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
SHARED_INTERFACE = NEXT_INTERFACE,
#endif
Expand All @@ -74,16 +81,23 @@ enum usb_interfaces {
#define MAX_INTERFACES 3

#if (NEXT_INTERFACE - 1) > MAX_INTERFACES
# error There are not enough available interfaces to support all functions. Please disable one or more of the following: Mouse Keys, Extra Keys, Raw HID, Console
# error There are not enough available interfaces to support all functions. Please disable one or more of the following: Mouse Keys, Extra Keys, NKRO, Raw HID, Console
#endif

#if (defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE)) && CONSOLE_ENABLE
# error Mouse/Extra Keys share an endpoint with Console. Please disable one of the two.
#endif

#if defined(NKRO_ENABLE) && defined(RAW_ENABLE)
# error NKRO shares an endpoint with Raw. Please disable one of the two.
#endif

static uint8_t keyboard_led_state = 0;
static uint8_t vusb_idle_rate = 0;

/* 0: Boot Protocol, 1: Report Protocol(default) */
uint8_t keyboard_protocol = 1;

/* Keyboard report send buffer */
#define KBUF_SIZE 16
static report_keyboard_t kbuf[KBUF_SIZE];
Expand All @@ -96,14 +110,38 @@ static report_keyboard_t keyboard_report_sent;

/* transfer keyboard report from buffer */
void vusb_transfer_keyboard(void) {
#ifdef NKRO_ENABLE
if (keyboard_protocol && keymap_config.nkro) {
for (int i = 0; i < VUSB_TRANSFER_KEYBOARD_MAX_TRIES; i++) {
if (usbInterruptIsReady4()) {
if (kbuf_head != kbuf_tail) {
_Static_assert(sizeof(struct nkro_report) == 16, "NKRO report size must be 16");
// Hack similar to below, we do two interrupt transfers
// to send a 16-byte report through an 8-byte endpoint (VUSB's hardcoded size)
usbSetInterrupt4((void *)&kbuf[kbuf_tail], 8);
while (!usbInterruptIsReady4()) {
usbPoll();
}
usbSetInterrupt4((void *)&(kbuf[kbuf_tail].nkro.bits[7]), 8);
kbuf_tail = (kbuf_tail + 1) % KBUF_SIZE;
}
break;
}
usbPoll();
wait_ms(1);
}
return;
}
#endif

for (int i = 0; i < VUSB_TRANSFER_KEYBOARD_MAX_TRIES; i++) {
if (usbInterruptIsReady()) {
if (kbuf_head != kbuf_tail) {
#ifndef KEYBOARD_SHARED_EP
usbSetInterrupt((void *)&kbuf[kbuf_tail], sizeof(report_keyboard_t));
usbSetInterrupt((void *)&kbuf[kbuf_tail], KEYBOARD_REPORT_SIZE);
#else
// Ugly hack! :(
usbSetInterrupt((void *)&kbuf[kbuf_tail], sizeof(report_keyboard_t) - 1);
usbSetInterrupt((void *)&kbuf[kbuf_tail], KEYBOARD_REPORT_SIZE - 1);
while (!usbInterruptIsReady()) {
usbPoll();
}
Expand Down Expand Up @@ -245,6 +283,9 @@ static void send_keyboard(report_keyboard_t *report) {
// NOTE: send key strokes of Macro
usbPoll();
vusb_transfer_keyboard();
#ifdef NKRO_ENABLE
if (!keyboard_protocol || !keymap_config.nkro)
#endif
keyboard_report_sent = *report;
}

Expand Down Expand Up @@ -307,7 +348,7 @@ usbMsgLen_t usbFunctionSetup(uchar data[8]) {
dprint("GET_REPORT:");
if (rq->wIndex.word == KEYBOARD_INTERFACE) {
usbMsgPtr = (usbMsgPtr_t)&keyboard_report_sent;
return sizeof(keyboard_report_sent);
return KEYBOARD_REPORT_SIZE;
}
} else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
dprint("GET_IDLE:");
Expand All @@ -325,6 +366,13 @@ usbMsgLen_t usbFunctionSetup(uchar data[8]) {
last_req.len = rq->wLength.word;
}
return USB_NO_MSG; // to get data in usbFunctionWrite
} else if (rq->bRequest == USBRQ_HID_GET_PROTOCOL) {
dprint("GET_PROTOCOL:");
usbMsgPtr = (usbMsgPtr_t)&keyboard_protocol;
return 1;
} else if (rq->bRequest == USBRQ_HID_SET_PROTOCOL) {
keyboard_protocol = rq->wValue.bytes[0];
dprintf("SET_PROTOCOL: %02X", keyboard_protocol);
} else {
dprint("UNKNOWN:");
}
Expand Down Expand Up @@ -542,6 +590,35 @@ const PROGMEM uchar raw_hid_report[] = {
};
#endif

#ifdef NKRO_ENABLE
const PROGMEM uchar nkro_hid_report[] = {
// NKRO report descriptor
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
// Modifiers (8 bits)
0x05, 0x07, // Usage Page (Keyboard/Keypad)
0x19, 0xE0, // Usage Minimum (Keyboard Left Control)
0x29, 0xE7, // Usage Maximum (Keyboard Right GUI)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x08, // Report Count (8)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
// Keycodes
0x05, 0x07, // Usage Page (Keyboard/Keypad)
0x19, 0x00, // Usage Minimum (0)
0x29, 15 * 8 - 1, // Usage Maximum
0x15, 0x00, // Logical Minimum (0)
0x26, 0x01, 0x00, // Logical Maximum (1)
0x95, 15 * 8, // Report Count
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
0xC0, // End Collection
};
#endif


#if defined(CONSOLE_ENABLE)
const PROGMEM uchar console_hid_report[] = {
0x06, 0x31, 0xFF, // Usage Page (Vendor Defined - PJRC Teensy compatible)
Expand Down Expand Up @@ -742,6 +819,46 @@ const PROGMEM usbConfigurationDescriptor_t usbConfigurationDescriptor = {
},
# endif

# if defined(NKRO_ENABLE)
/*
* N-key rollover
*/
.nkroInterface = {
.header = {
.bLength = sizeof(usbInterfaceDescriptor_t),
.bDescriptorType = USBDESCR_INTERFACE
},
.bInterfaceNumber = NKRO_INTERFACE,
.bAlternateSetting = 0x00,
.bNumEndpoints = 1,
.bInterfaceClass = 0x03,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01,
.iInterface = 0x00
},
.nkroHID = {
.header = {
.bLength = sizeof(usbHIDDescriptor_t),
.bDescriptorType = USBDESCR_HID
},
.bcdHID = 0x0101,
.bCountryCode = 0x00,
.bNumDescriptors = 1,
.bDescriptorType = USBDESCR_HID_REPORT,
.wDescriptorLength = sizeof(nkro_hid_report)
},
.nkroINEndpoint = {
.header = {
.bLength = sizeof(usbEndpointDescriptor_t),
.bDescriptorType = USBDESCR_ENDPOINT
},
.bEndpointAddress = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP4_NUMBER),
.bmAttributes = 0x03,
.wMaxPacketSize = 8,
.bInterval = USB_POLLING_INTERVAL_MS
},
# endif

# ifdef SHARED_EP_ENABLE
/*
* Shared
Expand Down Expand Up @@ -894,6 +1011,13 @@ USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) {
break;
#endif

#if defined(NKRO_ENABLE)
case NKRO_INTERFACE:
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.nkroHID;
len = sizeof(usbHIDDescriptor_t);
break;
#endif

#ifdef SHARED_EP_ENABLE
case SHARED_INTERFACE:
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.sharedHID;
Expand Down Expand Up @@ -926,6 +1050,13 @@ USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) {
break;
#endif

#if defined(NKRO_ENABLE)
case NKRO_INTERFACE:
usbMsgPtr = (usbMsgPtr_t)nkro_hid_report;
len = sizeof(nkro_hid_report);
break;
#endif

#ifdef SHARED_EP_ENABLE
case SHARED_INTERFACE:
usbMsgPtr = (usbMsgPtr_t)shared_hid_report;
Expand Down
6 changes: 6 additions & 0 deletions tmk_core/protocol/vusb/vusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ typedef struct usbConfigurationDescriptor {
usbEndpointDescriptor_t rawOUTEndpoint;
#endif

#if defined(NKRO_ENABLE)
usbInterfaceDescriptor_t nkroInterface;
usbHIDDescriptor_t nkroHID;
usbEndpointDescriptor_t nkroINEndpoint;
#endif

#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
usbInterfaceDescriptor_t sharedInterface;
usbHIDDescriptor_t sharedHID;
Expand Down