From 41f02c8f2597bcc15387a0ae37243b386c964602 Mon Sep 17 00:00:00 2001 From: Taylor Yu Date: Thu, 30 Nov 2023 11:35:29 -0600 Subject: [PATCH] hybrid boot keyboard Make the Boot Keyboard the only keyboard, and make it have a hybrid report format in Report Protoocol, while still ending Boot Reports in Boot Protocol. The hybrid reports have the Boot Report as a prefix, which might help hosts that don't request Boot Protocol but still expect to see it. Adjust USBQuirks to switch between always Boot Protocol and the hybrid report. Add visual feedback about the target mode to USBQuirks. Signed-off-by: Taylor Yu --- plugins/Kaleidoscope-USB-Quirks/README.md | 37 +- .../src/kaleidoscope/plugin/USB-Quirks.cpp | 50 +- .../src/kaleidoscope/plugin/USB-Quirks.h | 11 +- .../src/BootKeyboard/BootKeyboard.cpp | 465 ++++++++++++------ .../src/BootKeyboard/BootKeyboard.h | 60 ++- plugins/KeyboardioHID/src/KeyboardioHID.h | 1 - .../src/MultiReport/Keyboard.cpp | 305 ------------ .../KeyboardioHID/src/MultiReport/Keyboard.h | 77 --- .../virtual/DefaultHIDReportConsumer.cpp | 50 +- src/kaleidoscope/driver/hid/base/Keyboard.h | 114 +---- .../driver/hid/keyboardio/Keyboard.h | 58 +-- testing/HIDState.cpp | 4 +- testing/KeyboardReport.cpp | 2 +- testing/KeyboardReport.h | 8 +- 14 files changed, 514 insertions(+), 728 deletions(-) delete mode 100644 plugins/KeyboardioHID/src/MultiReport/Keyboard.cpp delete mode 100644 plugins/KeyboardioHID/src/MultiReport/Keyboard.h diff --git a/plugins/Kaleidoscope-USB-Quirks/README.md b/plugins/Kaleidoscope-USB-Quirks/README.md index dcd3f960ce..544bfeb430 100644 --- a/plugins/Kaleidoscope-USB-Quirks/README.md +++ b/plugins/Kaleidoscope-USB-Quirks/README.md @@ -1,6 +1,6 @@ # USB-Quirks -USB-Quirks provides a few methods to deal with more obscure parts of the USB spec, such as changing between `Boot` and `Report` protocols. These are in a separate plugin, because these features are not part of the USB spec, and are often workarounds for various issues. See the provided methods for more information about what they're useful for. +USB-Quirks provides a few methods to deal with more obscure parts of the USB spec, such as changing the behavior around the boot protocol. These are in a separate plugin, because these features are not part of the USB spec, and are often workarounds for various issues. See the provided methods for more information about what they're useful for. ## Using the plugin @@ -29,11 +29,32 @@ The plugin provides one object, `USBQuirks`, which provides the following method ### `.toggleKeyboardProtocol()` -> Toggle between `Boot` and `Report` protocol by detaching, and then -> re-attaching the USB devices, and setting the `BootKeyboard` protocol -> in between. +> Toggle whether the keyboard is able to send extended key reports (the +> default), or instead always sends boot reports, regardless of the +> protocol requested by the host. Switching the toggle causes the keyboard +> to detach and then re-attach to the host. (This re-attach is necessary to +> force re-enumeration with a different Report Descriptor.) > -> This is most useful when one needs to have a boot keyboard, when one's in a -> BIOS, boot loader, or early password prompt or the like, and the host does not -> explicitly request the boot protocol for one reason or the other. With this -> toggle, we can switch between the two on-demand. +> Switching the toggle also lights up a key indicating the mode being +> switched to: by default, `B` for boot reports only, and `N` for extended +> reports enabled. +> +> The extended key report supports n-key rollover (NKRO), and is actually a +> hybrid, having a prefix containing the boot report, for compatibility +> with older hosts. The boot report only supports 6-key rollover (6KRO), +> and is meant to support constrained hosts, such as BIOS, UEFI, or other +> pre-boot environments. The keyboard changes protocols as requested by the +> host. +> +> The USB HID specification requires that hosts explicitly request boot +> protocol if they need it, and that devices default to the non-boot +> protocol. Some hosts do not follow the specification, and expect boot +> protocol, even without requesting it. The backwards compatibility prefix +> of the hybrid extended report should accommodate some of these hosts. +> This toggle helps to work with hosts that neither request boot protocol +> nor tolerate the longer hybrid report. + +### `.setKeys(Key boot_led, Key nkro_led)` + +> Set which keys to light up to indicate the target mode. Defaults to +> `(Key_B, Key_N)`. diff --git a/plugins/Kaleidoscope-USB-Quirks/src/kaleidoscope/plugin/USB-Quirks.cpp b/plugins/Kaleidoscope-USB-Quirks/src/kaleidoscope/plugin/USB-Quirks.cpp index 095739cc8e..50f3eeeba2 100644 --- a/plugins/Kaleidoscope-USB-Quirks/src/kaleidoscope/plugin/USB-Quirks.cpp +++ b/plugins/Kaleidoscope-USB-Quirks/src/kaleidoscope/plugin/USB-Quirks.cpp @@ -23,16 +23,62 @@ #include "kaleidoscope/Runtime.h" // for Runtime, Runtime_ #include "kaleidoscope/device/device.h" // for Base<>::HID, VirtualProps::HID #include "kaleidoscope/driver/hid/keyboardio/Keyboard.h" // for Keyboard +#include "kaleidoscope/key_defs.h" // for Key +#include "kaleidoscope/plugin/LEDControl.h" // for LEDControl namespace kaleidoscope { namespace plugin { +KeyAddr USBQuirks::key_boot_addr = KeyAddr::none(); +KeyAddr USBQuirks::key_nkro_addr = KeyAddr::none(); + +static KeyAddr findKey(Key search_key) { + for (auto key_addr : KeyAddr::all()) { + Key k = Layer.lookupOnActiveLayer(key_addr); + + if (k == search_key) { + return key_addr; + } + } + return KeyAddr::none(); +} + +void USBQuirks::setKeys(Key boot_led, Key nkro_led) { + key_boot_addr = findKey(boot_led); + key_nkro_addr = findKey(nkro_led); +} + +EventHandlerResult USBQuirks::onSetup() { + setKeys(Key_B, Key_N); + return EventHandlerResult::OK; +} + void USBQuirks::toggleKeyboardProtocol() { - uint8_t new_protocol = !Runtime.hid().keyboard().getProtocol(); + KeyAddr key_addr; + uint8_t new_bootonly = !Runtime.hid().keyboard().getBootOnly(); + if (new_bootonly) { + key_addr = key_boot_addr; + } else { + key_addr = key_nkro_addr; + } + ::LEDControl.disable(); + if (key_addr.isValid()) { + ::LEDControl.setCrgbAt(key_addr, CRGB(0, 0, 255)); + } + Runtime.device().syncLeds(); + /* + * Release keys, because after detach, Windows 10 remembers keys that + * were pressed (from the MagicCombo that activated this function). + */ + Runtime.hid().keyboard().releaseAllKeys(); + Runtime.hid().keyboard().sendReport(); + delay(10); Runtime.detachFromHost(); - Runtime.hid().keyboard().setDefaultProtocol(new_protocol); + Runtime.hid().keyboard().setBootOnly(new_bootonly); delay(1000); + ::LEDControl.set_all_leds_to(CRGB(0, 0, 0)); + ::LEDControl.enable(); Runtime.attachToHost(); } diff --git a/plugins/Kaleidoscope-USB-Quirks/src/kaleidoscope/plugin/USB-Quirks.h b/plugins/Kaleidoscope-USB-Quirks/src/kaleidoscope/plugin/USB-Quirks.h index c8b5d4c99d..0847778386 100644 --- a/plugins/Kaleidoscope-USB-Quirks/src/kaleidoscope/plugin/USB-Quirks.h +++ b/plugins/Kaleidoscope-USB-Quirks/src/kaleidoscope/plugin/USB-Quirks.h @@ -17,13 +17,22 @@ #pragma once -#include "kaleidoscope/plugin.h" // for Plugin +#include "kaleidoscope/KeyAddr.h" // for KeyAddr +#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult +#include "kaleidoscope/key_defs.h" // for Key +#include "kaleidoscope/plugin.h" // for Plugin namespace kaleidoscope { namespace plugin { class USBQuirks : public kaleidoscope::Plugin { public: void toggleKeyboardProtocol(); + EventHandlerResult onSetup(); + static void setKeys(Key boot_led, Key nkro_led); + + private: + static KeyAddr key_boot_addr; + static KeyAddr key_nkro_addr; }; } // namespace plugin diff --git a/plugins/KeyboardioHID/src/BootKeyboard/BootKeyboard.cpp b/plugins/KeyboardioHID/src/BootKeyboard/BootKeyboard.cpp index 30a2dbcc7b..a5fcc4dce4 100644 --- a/plugins/KeyboardioHID/src/BootKeyboard/BootKeyboard.cpp +++ b/plugins/KeyboardioHID/src/BootKeyboard/BootKeyboard.cpp @@ -38,21 +38,36 @@ static const uint8_t boot_keyboard_hid_descriptor_[] PROGMEM = { D_COLLECTION, D_APPLICATION, - // Modifiers + + // LEDs + D_REPORT_COUNT, + 0x8, + D_REPORT_SIZE, + 0x1, D_USAGE_PAGE, - D_PAGE_KEYBOARD, + D_PAGE_LEDS, D_USAGE_MINIMUM, - 0xe0, + 0x1, D_USAGE_MAXIMUM, - 0xe7, + 0x8, D_LOGICAL_MINIMUM, 0x0, D_LOGICAL_MAXIMUM, 0x1, - D_REPORT_SIZE, - 0x1, - D_REPORT_COUNT, - 0x8, + D_OUTPUT, + (D_DATA | D_VARIABLE | D_ABSOLUTE), + + // Modifiers + D_USAGE_PAGE, + D_PAGE_KEYBOARD, + D_USAGE_MINIMUM, + HID_KEYBOARD_FIRST_MODIFIER, + D_USAGE_MAXIMUM, + HID_KEYBOARD_LAST_MODIFIER, + // D_LOGICAL_MINIMUM, 0x0, // redundant; already 0 + // D_LOGICAL_MAXIMUM, 0x1, // redundant; already 1 + // D_REPORT_SIZE, 0x1, // redundant; already 1 + // D_REPORT_COUNT, 0x8, // redundant; already 8 D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE), @@ -64,58 +79,114 @@ static const uint8_t boot_keyboard_hid_descriptor_[] PROGMEM = { D_INPUT, (D_CONSTANT), - // LEDs + // Non-modifiers D_REPORT_COUNT, - 0x5, - D_REPORT_SIZE, - 0x1, + BOOT_KEY_BYTES, + // D_REPORT_SIZE, 0x8, // redundant; already 8 + // D_LOGICAL_MINIMUM, HID_FIRST_KEY, // redundant; already 0 + D_MULTIBYTE(D_LOGICAL_MAXIMUM), + HID_LAST_KEY, + 0x0, // make sure it's not negative + // D_USAGE_PAGE, D_PAGE_KEYBOARD, // redundant; already KEYBOARD + D_USAGE_MINIMUM, + HID_FIRST_KEY, + D_USAGE_MAXIMUM, + HID_LAST_KEY, + D_INPUT, + (D_DATA | D_ARRAY | D_ABSOLUTE), + D_END_COLLECTION, +}; + +static const uint8_t hybrid_keyboard_hid_descriptor_[] PROGMEM = { + // Hybrid Boot/NKRO Keyboard + D_USAGE_PAGE, + D_PAGE_GENERIC_DESKTOP, + D_USAGE, + D_USAGE_KEYBOARD, + + D_COLLECTION, + D_APPLICATION, + + /* 5 LEDs for num lock etc, 3 left for advanced, custom usage */ D_USAGE_PAGE, D_PAGE_LEDS, D_USAGE_MINIMUM, - 0x1, + 0x01, D_USAGE_MAXIMUM, - 0x5, + 0x08, + D_LOGICAL_MINIMUM, + 0x00, + D_LOGICAL_MAXIMUM, + 0x01, + D_REPORT_SIZE, + 0x01, + D_REPORT_COUNT, + 0x08, D_OUTPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE), - // Pad LEDs up to a byte - D_REPORT_COUNT, - 0x1, + + D_USAGE_PAGE, + D_PAGE_KEYBOARD, + + /* Key modifier byte for both boot and NKRO */ + D_USAGE_MINIMUM, + HID_KEYBOARD_FIRST_MODIFIER, + D_USAGE_MAXIMUM, + HID_KEYBOARD_LAST_MODIFIER, + // D_LOGICAL_MINIMUM, 0x00, // redundant; already 0 + // D_LOGICAL_MAXIMUM, 0x01, // redundant; already 1 + // D_REPORT_SIZE, 0x01, // redundant; already 1 + // D_REPORT_COUNT, 0x08, // redundant; already 8 + D_INPUT, + (D_DATA | D_VARIABLE | D_ABSOLUTE), + + /* Send rest of boot report as padding, so HID-aware hosts will ignore */ D_REPORT_SIZE, - 0x3, - D_OUTPUT, + 0x8, + D_REPORT_COUNT, + 0x7, + D_INPUT, (D_CONSTANT), - // Non-modifiers - D_REPORT_COUNT, - 0x6, + /* NKRO key bitmap */ + + // Padding 4 bits, to skip NO_EVENT & 3 error states. D_REPORT_SIZE, - 0x8, - D_LOGICAL_MINIMUM, - 0x0, - D_MULTIBYTE(D_LOGICAL_MAXIMUM), - 0xff, - 0x0, // make sure it's not negative - D_USAGE_PAGE, - D_PAGE_KEYBOARD, + 0x1, + D_REPORT_COUNT, + 0x04, + D_INPUT, + (D_CONSTANT), + + // Actual non-modifier keys D_USAGE_MINIMUM, - 0x0, + HID_KEYBOARD_A_AND_A, D_USAGE_MAXIMUM, - 0xff, + HID_LAST_KEY, + // D_LOGICAL_MINIMUM, 0x00, // redundant; already 0 + // D_LOGICAL_MAXIMUM, 0x01, // redundant; already 1 + // D_REPORT_SIZE, 0x01, // redundant; already 1 + D_REPORT_COUNT, + (NKRO_KEY_BITS - 4), D_INPUT, - (D_DATA | D_ARRAY | D_ABSOLUTE), - D_END_COLLECTION}; + (D_DATA | D_VARIABLE | D_ABSOLUTE), -#ifdef ARCH_HAS_CONFIGURABLE_EP_SIZES -static const uint8_t BOOT_KEYBOARD_EP_SIZE = 8; -#else -static const uint8_t BOOT_KEYBOARD_EP_SIZE = USB_EP_SIZE; +#if (NKRO_KEY_BITS % 8) + // Padding to round up the report to byte boundary. + // D_REPORT_SIZE, 0x01, // redundant; already 1 + D_REPORT_COUNT, + (8 - (NKRO_KEY_BITS % 8)), + D_INPUT, + (D_CONSTANT), #endif + D_END_COLLECTION, +}; -BootKeyboard_::BootKeyboard_(uint8_t protocol_) - : PluggableUSBModule(1, 1, epType), default_protocol(protocol_), protocol(protocol_), idle(0), leds(0) { +BootKeyboard_::BootKeyboard_(uint8_t bootkb_only_) + : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(0), leds(0), bootkb_only(bootkb_only) { #ifdef ARCH_HAS_CONFIGURABLE_EP_SIZES - epType[0] = EP_TYPE_INTERRUPT_IN(BOOT_KEYBOARD_EP_SIZE); // This is an 8 byte report, so ask for an 8 byte buffer, so reports aren't split + epType[0] = EP_TYPE_INTERRUPT_IN(USB_EP_SIZE); #else epType[0] = EP_TYPE_INTERRUPT_IN; #endif @@ -124,10 +195,17 @@ BootKeyboard_::BootKeyboard_(uint8_t protocol_) int BootKeyboard_::getInterface(uint8_t *interfaceCount) { *interfaceCount += 1; // uses 1 + size_t desclen; + if (bootkb_only) { + desclen = sizeof(boot_keyboard_hid_descriptor_); + } else { + desclen = sizeof(hybrid_keyboard_hid_descriptor_); + } HIDDescriptor hidInterface = { D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_BOOT_INTERFACE, HID_PROTOCOL_KEYBOARD), - D_HIDREPORT(sizeof(boot_keyboard_hid_descriptor_)), - D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, BOOT_KEYBOARD_EP_SIZE, 0x01)}; + D_HIDREPORT(desclen), + D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01), + }; return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); } @@ -145,7 +223,11 @@ int BootKeyboard_::getDescriptor(USBSetup &setup) { return 0; } - return USB_SendControl(TRANSFER_PGM, boot_keyboard_hid_descriptor_, sizeof(boot_keyboard_hid_descriptor_)); + if (bootkb_only) { + return USB_SendControl(TRANSFER_PGM, boot_keyboard_hid_descriptor_, sizeof(boot_keyboard_hid_descriptor_)); + } else { + return USB_SendControl(TRANSFER_PGM, hybrid_keyboard_hid_descriptor_, sizeof(hybrid_keyboard_hid_descriptor_)); + } } @@ -214,12 +296,6 @@ bool BootKeyboard_::setup(USBSetup &setup) { USB_RecvControl(&leds, length); return true; } - // Input (set HID report) - } else if (setup.wValueH == HID_REPORT_TYPE_INPUT) { - if (length == sizeof(report_)) { - USB_RecvControl(&report_, length); - return true; - } } } } @@ -235,130 +311,141 @@ uint8_t BootKeyboard_::getProtocol() { return protocol; } -void BootKeyboard_::setProtocol(uint8_t protocol) { - this->protocol = protocol; +uint8_t BootKeyboard_::getBootOnly() { + return bootkb_only; } -int BootKeyboard_::sendReport() { - if (memcmp(&last_report_, &report_, sizeof(report_))) { - // if the two reports are different, send a report - int returnCode = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &report_, sizeof(report_)); - HIDReportObserver::observeReport(HID_REPORTID_KEYBOARD, &report_, sizeof(report_), returnCode); - memcpy(&last_report_, &report_, sizeof(report_)); - return returnCode; - } - return -1; +/* + * Set whether to send only Boot Protocol regardless of whether Boot + * Protocol has been requested. Used by USBQuirks to toggle protocol + * modes. + * + * This is only really safe to call after detaching from USB, because + * otherwise, the report format might get out of sync with what the host + * expects, and the host probably won't see the new Report Descriptor + * until after a re-attach. + */ +void BootKeyboard_::setBootOnly(uint8_t bootonly) { + bootkb_only = bootonly; } -// press() adds the specified key (printing, non-printing, or modifier) -// to the persistent key report and sends the report. Because of the way -// USB HID works, the host acts like the key remains pressed until we -// call release(), releaseAll(), or otherwise clear the report and resend. - -size_t BootKeyboard_::press(uint8_t k) { - uint8_t done = 0; - - if ((k >= HID_KEYBOARD_FIRST_MODIFIER) && (k <= HID_KEYBOARD_LAST_MODIFIER)) { - // it's a modifier key - report_.modifiers |= (0x01 << (k - HID_KEYBOARD_FIRST_MODIFIER)); - } else { - // it's some other key: - // Add k to the key report only if it's not already present - // and if there is an empty slot. - for (uint8_t i = 0; i < sizeof(report_.keycodes); i++) { - if (report_.keycodes[i] != k) { // is k already in list? - if (0 == report_.keycodes[i]) { // have we found an empty slot? - report_.keycodes[i] = k; - done = 1; - break; - } - } else { - done = 1; - break; - } - } - // use separate variable to check if slot was found - // for style reasons - we do not know how the compiler - // handles the for() index when it leaves the loop - if (0 == done) { - return 0; +void BootKeyboard_::convertReport(uint8_t *boot, const uint8_t *nkro) { + uint8_t n_boot_keys = 0; + uint8_t b; + // Convert NKRO report to boot report + memset(boot, HID_KEYBOARD_NO_EVENT, BOOT_KEY_BYTES); + for (uint8_t i = 0; i < NKRO_KEY_BYTES; i++) { + b = nkro[i]; + if (b == 0) { + continue; } - } - return 1; -} - - -// release() takes the specified key out of the persistent key report and -// sends the report. This tells the OS the key is no longer pressed and that -// it shouldn't be repeated any more. - -size_t BootKeyboard_::release(uint8_t k) { - if ((k >= HID_KEYBOARD_FIRST_MODIFIER) && (k <= HID_KEYBOARD_LAST_MODIFIER)) { - // it's a modifier key - report_.modifiers = report_.modifiers & (~(0x01 << (k - HID_KEYBOARD_FIRST_MODIFIER))); - } else { - // it's some other key: - // Test the key report to see if k is present. Clear it if it exists. - // Check all positions in case the key is present more than once (which it shouldn't be) - for (uint8_t i = 0; i < sizeof(report_.keycodes); i++) { - if (report_.keycodes[i] == k) { - report_.keycodes[i] = 0; + for (uint8_t j = 0; j < 8; j++) { + bool bit = b & 1; + b >>= 1; + if (bit == 0) { + continue; } - } - - // rearrange the keys list so that the free (= 0x00) are at the - // end of the keys list - some implementations stop for keys at the - // first occurence of an 0x00 in the keys list - // so (0x00)(0x01)(0x00)(0x03)(0x02)(0x00) becomes - // (0x03)(0x02)(0x01)(0x00)(0x00)(0x00) - uint8_t current = 0, nextpos = 0; - - while (current < sizeof(report_.keycodes)) { - if (report_.keycodes[current]) { - uint8_t tmp = report_.keycodes[nextpos]; - report_.keycodes[nextpos] = report_.keycodes[current]; - report_.keycodes[current] = tmp; - ++nextpos; + // Check is here so we can set all BOOT_KEY_BYTES + if (n_boot_keys >= BOOT_KEY_BYTES) { + // Send rollover error if too many keys are held + memset(boot, HID_KEYBOARD_ERROR_ROLLOVER, BOOT_KEY_BYTES); + return; } - ++current; + boot[n_boot_keys++] = 8 * i + j; } } - - return 1; } - -void BootKeyboard_::releaseAll() { - memset(&report_.bytes, 0x00, sizeof(report_.bytes)); +/* Send a report without the extra modifier change handling */ +int BootKeyboard_::sendReportUnchecked() { + HID_BootKeyboardReport_Data_t out_report; + out_report.modifiers = last_report_.modifiers; + out_report.reserved = 0; + memcpy(out_report.nkro_keys, last_report_.keys, sizeof(last_report_.keys)); + convertReport(out_report.boot_keycodes, out_report.nkro_keys); + size_t reportlen; + // Send only boot report if host requested boot protocol, or if configured as boot-only + if (protocol == HID_BOOT_PROTOCOL || bootkb_only) { + reportlen = BOOT_REPORT_LEN; + } else { + reportlen = sizeof(out_report); + } + int returnCode = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &out_report, reportlen); + HIDReportObserver::observeReport(HID_REPORTID_KEYBOARD, &out_report, reportlen, returnCode); + return returnCode; } +// Sending the current HID report to the host: +// +// Depending on the differences between the current and previous HID reports, we +// might need to send one or two extra reports to guarantee that the host will +// process the changes in the correct order. There are two important scenarios +// to consider: +// +// 1. If a non-modifier keycode toggles off in the same report as a modifier +// changes, the host might process the modifier change first. For example, if +// both `shift` and `4` toggle off in the same report (most likely from a +// `LSHIFT(Key_4)` key being released), and that key has been held long enough +// to trigger character repeat, we could end up with a plain `4` in the output +// at the end of the repeat: `$$$$4` instead of `$$$$$`. +// +// 2. If a non-modifier keycode toggles on in the same report as a modifier +// changes, the host might process the non-modifer first. For example, pressing +// and holding an `LSHIFT(Key_4)` key might result in `4$$$` rather than `$$$$`. +// +// Therefore, each call to `sendReport()` must send (up to) three reports to the +// host to guarantee the correct order of processing: +// +// 1. A report with toggled-off non-modifiers removed. +// 2. A report with changes to modifiers. +// 3. A report with toggled-on non-modifiers added. -/* Returns true if the non-modifer key passed in will be sent during this key report - * Returns false in all other cases - * */ -bool BootKeyboard_::isKeyPressed(uint8_t k) { - for (uint8_t i = 0; i < sizeof(report_.keycodes); i++) { - if (report_.keycodes[i] == k) { - return true; +int BootKeyboard_::sendReport() { + // If the new HID report differs from the previous one both in active modifier + // keycodes and non-modifier keycodes, we will need to send at least one extra + // report. First, we compare the modifiers bytes of the two reports. + const uint8_t old_modifiers = last_report_.modifiers; + const uint8_t new_modifiers = report_.modifiers; + + const uint8_t changed_modifiers = old_modifiers ^ new_modifiers; + + if (changed_modifiers != 0) { + // There was at least one modifier change (toggled on or off), remove any + // non-modifiers from the stored previous report that toggled off in the new + // report, and send it to the host. + bool non_modifiers_toggled_off = false; + for (uint8_t i = 0; i < NKRO_KEY_BYTES; ++i) { + byte released_keycodes = last_report_.keys[i] & ~(report_.keys[i]); + if (released_keycodes != 0) { + last_report_.keys[i] &= ~released_keycodes; + non_modifiers_toggled_off = true; + } + } + if (non_modifiers_toggled_off) { + sendReportUnchecked(); } + // Next, update the modifiers byte of the stored previous report, and send + // it. + last_report_.modifiers = new_modifiers; + sendReportUnchecked(); } - return false; -} -/* Returns true if the non-modifer key passed in was sent during the previous key report - * Returns false in all other cases - * */ -bool BootKeyboard_::wasKeyPressed(uint8_t k) { - for (uint8_t i = 0; i < sizeof(report_.keycodes); i++) { - if (last_report_.keycodes[i] == k) { - return true; - } + // Finally, copy the new report to the previous one, and send it. + if (memcmp(last_report_.keys, report_.keys, sizeof(report_.keys)) != 0) { + memcpy(last_report_.keys, report_.keys, sizeof(report_.keys)); + return sendReportUnchecked(); } - return false; + // A note on return values: Kaleidoscope doesn't actually check the return + // value of `sendReport()`, so this function could be changed to return + // void. It would be nice if we could do something to recover from an error + // here, but it's not at all clear what that should be. Also note that if the + // extra reports above return an error, there's not much we can do to try to + // recover. We could try to send the report again, but that would be likely to + // fail as well. + return -1; } - /* Returns true if the modifer key passed in will be sent during this key report * Returns false in all other cases * */ @@ -381,20 +468,86 @@ bool BootKeyboard_::wasModifierActive(uint8_t k) { return false; } -/* Returns true if any modifier key will be sent during this key report +/* Returns true if *any* modifier will be sent during this key report * Returns false in all other cases * */ bool BootKeyboard_::isAnyModifierActive() { return report_.modifiers > 0; } -/* Returns true if any modifier key was being sent during the previous key report +/* Returns true if *any* modifier was being sent during the previous key report * Returns false in all other cases * */ bool BootKeyboard_::wasAnyModifierActive() { return last_report_.modifiers > 0; } + +/* Returns true if the non-modifier key passed in will be sent during this key report + * Returns false in all other cases + * */ +bool BootKeyboard_::isKeyPressed(uint8_t k) { + if (k <= HID_LAST_KEY) { + uint8_t bit = 1 << (uint8_t(k) % 8); + return !!(report_.keys[k / 8] & bit); + } + return false; +} + +/* Returns true if the non-modifer key passed in was sent during the previous key report + * Returns false in all other cases + * */ +bool BootKeyboard_::wasKeyPressed(uint8_t k) { + + if (k <= HID_LAST_KEY) { + uint8_t bit = 1 << (uint8_t(k) % 8); + return !!(last_report_.keys[k / 8] & bit); + } + return false; +} + + +size_t BootKeyboard_::press(uint8_t k) { + // If the key is in the range of 'printable' keys + if (k <= HID_LAST_KEY) { + uint8_t bit = 1 << (uint8_t(k) % 8); + report_.keys[k / 8] |= bit; + return 1; + } else if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { + // It's a modifier key + // Convert key into bitfield (0 - 7) + k = k - HID_KEYBOARD_FIRST_MODIFIER; + report_.modifiers |= (1 << k); + return 1; + } + + // No empty/pressed key was found + return 0; +} + +size_t BootKeyboard_::release(uint8_t k) { + // If we're releasing a printable key + if (k <= HID_LAST_KEY) { + uint8_t bit = 1 << (k % 8); + report_.keys[k / 8] &= ~bit; + return 1; + } else if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { + // It's a modifier key + // Convert key into bitfield (0 - 7) + k = k - HID_KEYBOARD_FIRST_MODIFIER; + report_.modifiers &= ~(1 << k); + return 1; + } + + // No empty/pressed key was found + return 0; +} + +void BootKeyboard_::releaseAll() { + // Release all keys + memset(&report_.allkeys, 0x00, sizeof(report_.allkeys)); +} + /* * Hook function to reset any needed state after a USB reset. * @@ -402,7 +555,7 @@ bool BootKeyboard_::wasAnyModifierActive() { * required by the HID specification. */ void BootKeyboard_::onUSBReset() { - protocol = default_protocol; + protocol = HID_REPORT_PROTOCOL; } __attribute__((weak)) diff --git a/plugins/KeyboardioHID/src/BootKeyboard/BootKeyboard.h b/plugins/KeyboardioHID/src/BootKeyboard/BootKeyboard.h index d83f6c734e..69471d0da6 100644 --- a/plugins/KeyboardioHID/src/BootKeyboard/BootKeyboard.h +++ b/plugins/KeyboardioHID/src/BootKeyboard/BootKeyboard.h @@ -31,20 +31,59 @@ THE SOFTWARE. #include "HIDTables.h" #include "HIDAliases.h" +// Declare the hybrid boot keyboard feature so Kaleidoscope can depend on it +#define BOOTKB_HYBRID 1 + +#define BOOT_KEY_BYTES 6 +#define BOOT_REPORT_LEN 8 + +#define NKRO_KEY_BITS (4 + HID_LAST_KEY - HID_KEYBOARD_A_AND_A + 1) +#define NKRO_KEY_BYTES ((NKRO_KEY_BITS + 7) / 8) + +/* + * Keep the current key states in a NKRO bitmap. We'll convert it to Boot + * Protocol as needed. + */ typedef union { - // Low level key report: up to 6 keys and shift, ctrl etc at once + // Modifiers + keymap struct { + uint8_t modifiers; + uint8_t keys[NKRO_KEY_BYTES]; + }; + uint8_t allkeys[1 + NKRO_KEY_BYTES]; +} HID_NKRO_KeyboardReport_Data_t; + +/* + * Hybrid boot report that contains the Boot Protocol report as a prefix to + * a bitmap NKRO report. The keycodes array of the Boot Report is marked as + * padding in the Report Descriptor, so HID-aware hosts will ignore it, but + * hosts that require Boot Protocol without requesting it have a chance of + * working, assuming they can deal with the extended report. + * + * We do send only the Boot Report if the host has requested Boot Protocol. + */ +typedef union { + // Hybrid report: boot keyboard report + NKRO report + struct { + // Boot/NKRO keyboard modifiers uint8_t modifiers; uint8_t reserved; - uint8_t keycodes[6]; + // Boot keyboard non-modifier keys array + uint8_t boot_keycodes[BOOT_KEY_BYTES]; + // NKRO keyboard non-modifiers keys bitmap + uint8_t nkro_keys[NKRO_KEY_BYTES]; }; - uint8_t bytes[8]; + uint8_t bytes[BOOT_REPORT_LEN + NKRO_KEY_BYTES]; } HID_BootKeyboardReport_Data_t; class BootKeyboard_ : public PluggableUSBModule { public: - explicit BootKeyboard_(uint8_t protocol_ = HID_REPORT_PROTOCOL); + /* + * Change to `bootkb_only_ = 1` if you need to default to only sending + * Boot Protocol, even if in Report Protocol. + */ + explicit BootKeyboard_(uint8_t bootkb_only_ = 0); size_t press(uint8_t k); void begin(); void end(); @@ -62,13 +101,14 @@ class BootKeyboard_ : public PluggableUSBModule { uint8_t getLeds(); uint8_t getProtocol(); - void setProtocol(uint8_t protocol); - uint8_t default_protocol; + uint8_t getBootOnly(); + void setBootOnly(uint8_t bootonly); + void onUSBReset(); protected: - HID_BootKeyboardReport_Data_t report_, last_report_; + HID_NKRO_KeyboardReport_Data_t report_, last_report_; // Implementation of the PUSBListNode int getInterface(uint8_t *interfaceCount); @@ -80,5 +120,11 @@ class BootKeyboard_ : public PluggableUSBModule { uint8_t idle; uint8_t leds; + + uint8_t bootkb_only; + + private: + void convertReport(uint8_t *boot, const uint8_t *nkro); + int sendReportUnchecked(); }; extern BootKeyboard_ &BootKeyboard(); diff --git a/plugins/KeyboardioHID/src/KeyboardioHID.h b/plugins/KeyboardioHID/src/KeyboardioHID.h index 7f68e208b5..5e700cb525 100644 --- a/plugins/KeyboardioHID/src/KeyboardioHID.h +++ b/plugins/KeyboardioHID/src/KeyboardioHID.h @@ -47,7 +47,6 @@ THE SOFTWARE. #include "MultiReport/ConsumerControl.h" #include "MultiReport/Gamepad.h" #include "MultiReport/SystemControl.h" -#include "MultiReport/Keyboard.h" #include "SingleReport/SingleAbsoluteMouse.h" diff --git a/plugins/KeyboardioHID/src/MultiReport/Keyboard.cpp b/plugins/KeyboardioHID/src/MultiReport/Keyboard.cpp deleted file mode 100644 index 2b24cfea7d..0000000000 --- a/plugins/KeyboardioHID/src/MultiReport/Keyboard.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* -Copyright (c) 2014-2015 NicoHood -Copyright (c) 2015-2018 Keyboard.io, Inc - -See the readme for credit to other people. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include "Keyboard.h" -#include "DescriptorPrimitives.h" - -static const uint8_t nkro_keyboard_hid_descriptor_[] PROGMEM = { - // NKRO Keyboard - D_USAGE_PAGE, - D_PAGE_GENERIC_DESKTOP, - D_USAGE, - D_USAGE_KEYBOARD, - D_COLLECTION, - D_APPLICATION, - D_REPORT_ID, - HID_REPORTID_NKRO_KEYBOARD, - D_USAGE_PAGE, - D_PAGE_KEYBOARD, - - /* Key modifier byte */ - D_USAGE_MINIMUM, - HID_KEYBOARD_FIRST_MODIFIER, - D_USAGE_MAXIMUM, - HID_KEYBOARD_LAST_MODIFIER, - D_LOGICAL_MINIMUM, - 0x00, - D_LOGICAL_MAXIMUM, - 0x01, - D_REPORT_SIZE, - 0x01, - D_REPORT_COUNT, - 0x08, - D_INPUT, - (D_DATA | D_VARIABLE | D_ABSOLUTE), - - /* 5 LEDs for num lock etc, 3 left for advanced, custom usage */ - D_USAGE_PAGE, - D_PAGE_LEDS, - D_USAGE_MINIMUM, - 0x01, - D_USAGE_MAXIMUM, - 0x08, - D_REPORT_COUNT, - 0x08, - D_REPORT_SIZE, - 0x01, - D_OUTPUT, - (D_DATA | D_VARIABLE | D_ABSOLUTE), - - /* NKRO Keyboard */ - D_USAGE_PAGE, - D_PAGE_KEYBOARD, - - // Padding 4 bits, to skip NO_EVENT & 3 error states. - D_REPORT_SIZE, - 0x04, - D_REPORT_COUNT, - 0x01, - D_INPUT, - (D_CONSTANT), - - D_USAGE_MINIMUM, - HID_KEYBOARD_A_AND_A, - D_USAGE_MAXIMUM, - HID_LAST_KEY, - D_LOGICAL_MINIMUM, - 0x00, - D_LOGICAL_MAXIMUM, - 0x01, - D_REPORT_SIZE, - 0x01, - D_REPORT_COUNT, - (KEY_BITS - 4), - D_INPUT, - (D_DATA | D_VARIABLE | D_ABSOLUTE), - -#if (KEY_BITS % 8) - // Padding to round up the report to byte boundary. - D_REPORT_SIZE, - (8 - (KEY_BITS % 8)), - D_REPORT_COUNT, - 0x01, - D_INPUT, - (D_CONSTANT), -#endif - - D_END_COLLECTION, -}; - -Keyboard_::Keyboard_() { - static HIDSubDescriptor node(nkro_keyboard_hid_descriptor_, - sizeof(nkro_keyboard_hid_descriptor_)); - HID().AppendDescriptor(&node); -} - -void Keyboard_::begin() { -} - - -void Keyboard_::end() { - releaseAll(); - sendReportUnchecked(); -} - -int Keyboard_::sendReportUnchecked() { - return HID().SendReport(HID_REPORTID_NKRO_KEYBOARD, - &last_report_, - sizeof(last_report_)); -} - -// Sending the current HID report to the host: -// -// Depending on the differences between the current and previous HID reports, we -// might need to send one or two extra reports to guarantee that the host will -// process the changes in the correct order. There are two important scenarios -// to consider: -// -// 1. If a non-modifier keycode toggles off in the same report as a modifier -// changes, the host might process the modifier change first. For example, if -// both `shift` and `4` toggle off in the same report (most likely from a -// `LSHIFT(Key_4)` key being released), and that key has been held long enough -// to trigger character repeat, we could end up with a plain `4` in the output -// at the end of the repeat: `$$$$4` instead of `$$$$$`. -// -// 2. If a non-modifier keycode toggles on in the same report as a modifier -// changes, the host might process the non-modifer first. For example, pressing -// and holding an `LSHIFT(Key_4)` key might result in `4$$$` rather than `$$$$`. -// -// Therefore, each call to `sendReport()` must send (up to) three reports to the -// host to guarantee the correct order of processing: -// -// 1. A report with toggled-off non-modifiers removed. -// 2. A report with changes to modifiers. -// 3. A report with toggled-on non-modifiers added. - -int Keyboard_::sendReport() { - // If the new HID report differs from the previous one both in active modifier - // keycodes and non-modifier keycodes, we will need to send at least one extra - // report. First, we compare the modifiers bytes of the two reports. - const uint8_t old_modifiers = last_report_.modifiers; - const uint8_t new_modifiers = report_.modifiers; - - const uint8_t changed_modifiers = old_modifiers ^ new_modifiers; - - if (changed_modifiers != 0) { - // There was at least one modifier change (toggled on or off), remove any - // non-modifiers from the stored previous report that toggled off in the new - // report, and send it to the host. - bool non_modifiers_toggled_off = false; - for (uint8_t i = 0; i < KEY_BYTES; ++i) { - byte released_keycodes = last_report_.keys[i] & ~(report_.keys[i]); - if (released_keycodes != 0) { - last_report_.keys[i] &= ~released_keycodes; - non_modifiers_toggled_off = true; - } - } - if (non_modifiers_toggled_off) { - sendReportUnchecked(); - } - // Next, update the modifiers byte of the stored previous report, and send - // it. - last_report_.modifiers = new_modifiers; - sendReportUnchecked(); - } - - // Finally, copy the new report to the previous one, and send it. - if (memcmp(last_report_.keys, report_.keys, sizeof(report_.keys)) != 0) { - memcpy(last_report_.keys, report_.keys, sizeof(report_.keys)); - return sendReportUnchecked(); - } - // A note on return values: Kaleidoscope doesn't actually check the return - // value of `sendReport()`, so this function could be changed to return - // void. It would be nice if we could do something to recover from an error - // here, but it's not at all clear what that should be. Also note that if the - // extra reports above return an error, there's not much we can do to try to - // recover. We could try to send the report again, but that would be likely to - // fail as well. - return -1; -} - -/* Returns true if the modifer key passed in will be sent during this key report - * Returns false in all other cases - * */ -bool Keyboard_::isModifierActive(uint8_t k) { - if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { - k = k - HID_KEYBOARD_FIRST_MODIFIER; - return !!(report_.modifiers & (1 << k)); - } - return false; -} - -/* Returns true if the modifer key passed in was being sent during the previous key report - * Returns false in all other cases - * */ -bool Keyboard_::wasModifierActive(uint8_t k) { - if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { - k = k - HID_KEYBOARD_FIRST_MODIFIER; - return !!(last_report_.modifiers & (1 << k)); - } - return false; -} - -/* Returns true if *any* modifier will be sent during this key report - * Returns false in all other cases - * */ -bool Keyboard_::isAnyModifierActive() { - return report_.modifiers > 0; -} - -/* Returns true if *any* modifier was being sent during the previous key report - * Returns false in all other cases - * */ -bool Keyboard_::wasAnyModifierActive() { - return last_report_.modifiers > 0; -} - - -/* Returns true if the non-modifier key passed in will be sent during this key report - * Returns false in all other cases - * */ -bool Keyboard_::isKeyPressed(uint8_t k) { - if (k <= HID_LAST_KEY) { - uint8_t bit = 1 << (uint8_t(k) % 8); - return !!(report_.keys[k / 8] & bit); - } - return false; -} - -/* Returns true if the non-modifer key passed in was sent during the previous key report - * Returns false in all other cases - * */ -bool Keyboard_::wasKeyPressed(uint8_t k) { - - if (k <= HID_LAST_KEY) { - uint8_t bit = 1 << (uint8_t(k) % 8); - return !!(last_report_.keys[k / 8] & bit); - } - return false; -} - - -size_t Keyboard_::press(uint8_t k) { - // If the key is in the range of 'printable' keys - if (k <= HID_LAST_KEY) { - uint8_t bit = 1 << (uint8_t(k) % 8); - report_.keys[k / 8] |= bit; - return 1; - // It's a modifier key - } else if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { - // Convert key into bitfield (0 - 7) - k = k - HID_KEYBOARD_FIRST_MODIFIER; - report_.modifiers |= (1 << k); - return 1; - } - - // No empty/pressed key was found - return 0; -} - -size_t Keyboard_::release(uint8_t k) { - // If we're releasing a printable key - if (k <= HID_LAST_KEY) { - uint8_t bit = 1 << (k % 8); - report_.keys[k / 8] &= ~bit; - return 1; - // It's a modifier key - } else if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { - // Convert key into bitfield (0 - 7) - k = k - HID_KEYBOARD_FIRST_MODIFIER; - report_.modifiers &= ~(1 << k); - return 1; - } - - // No empty/pressed key was found - return 0; -} - -void Keyboard_::releaseAll() { - // Release all keys - memset(&report_.allkeys, 0x00, sizeof(report_.allkeys)); -} - -Keyboard_ Keyboard; diff --git a/plugins/KeyboardioHID/src/MultiReport/Keyboard.h b/plugins/KeyboardioHID/src/MultiReport/Keyboard.h deleted file mode 100644 index e5dffc53a2..0000000000 --- a/plugins/KeyboardioHID/src/MultiReport/Keyboard.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright (c) 2014-2015 NicoHood -Copyright (c) 2015-2018 Keyboard.io, Inc - -See the readme for credit to other people. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -// Include guard -#pragma once - -#include -#include "HID.h" -#include "HID-Settings.h" - -#include "HIDTables.h" -#include "HIDAliases.h" - -#define KEY_BITS (4 + HID_LAST_KEY - HID_KEYBOARD_A_AND_A + 1) -#define KEY_BYTES ((KEY_BITS + 7) / 8) - -typedef union { - // Modifiers + keymap - struct { - uint8_t modifiers; - uint8_t keys[KEY_BYTES]; - }; - uint8_t allkeys[1 + KEY_BYTES]; -} HID_KeyboardReport_Data_t; - - -class Keyboard_ { - public: - Keyboard_(); - void begin(); - void end(); - - size_t press(uint8_t k); - size_t release(uint8_t k); - void releaseAll(); - int sendReport(); - - bool isKeyPressed(uint8_t k); - bool wasKeyPressed(uint8_t k); - bool isModifierActive(uint8_t k); - bool wasModifierActive(uint8_t k); - bool isAnyModifierActive(); - bool wasAnyModifierActive(); - - uint8_t getLEDs() { - return HID().getLEDs(); - } - - private: - HID_KeyboardReport_Data_t report_; - HID_KeyboardReport_Data_t last_report_; - - int sendReportUnchecked(); -}; -extern Keyboard_ Keyboard; diff --git a/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.cpp b/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.cpp index 83d4cd9cfd..9991a2d824 100644 --- a/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.cpp +++ b/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.cpp @@ -19,8 +19,8 @@ #include "kaleidoscope/device/virtual/DefaultHIDReportConsumer.h" // From KeyboardioHID: -#include // for HID_REPORTID_NKRO_KEYBOARD -#include // for HID_KeyboardReport_Data_t, (anonymous u... +#include // for HID_REPORTID_NKRO_KEYBOARD +#include // for HID_BootKeyboardReport_Data_t, (anonymous u... // From system: #include // for uint8_t // From Arduino core: @@ -52,12 +52,12 @@ using namespace logging; // NOLINT(build/namespaces) void DefaultHIDReportConsumer::processHIDReport( uint8_t id, const void *data, int len, int result) { - if (id != HID_REPORTID_NKRO_KEYBOARD) { + if (id != HID_REPORTID_KEYBOARD) { log_info("***Ignoring hid report with id = %d\n", id); return; } - const HID_KeyboardReport_Data_t &report_data = *static_cast(data); + const HID_BootKeyboardReport_Data_t &report_data = *static_cast(data); std::stringstream keypresses; bool anything = false; @@ -65,8 +65,8 @@ void DefaultHIDReportConsumer::processHIDReport( if (report_data.modifiers) { anything = true; } else { - for (int i = 0; i < KEY_BYTES; i++) { - if (report_data.keys[i]) { + for (int i = 0; i < NKRO_KEY_BYTES; i++) { + if (report_data.nkro_keys[i]) { anything = true; break; } @@ -80,53 +80,53 @@ void DefaultHIDReportConsumer::processHIDReport( FOREACHBIT(report_data.modifiers, keypresses, "lctrl ", "lshift ", "lalt ", "lgui ", "rctrl ", "rshift ", "ralt ", "rgui ") - FOREACHBIT(report_data.keys[0], keypresses, + FOREACHBIT(report_data.nkro_keys[0], keypresses, "NO_EVENT ", "ERROR_ROLLOVER ", "POST_FAIL ", "ERROR_UNDEFINED ", "a ", "b ", "c ", "d ") - FOREACHBIT(report_data.keys[1], keypresses, + FOREACHBIT(report_data.nkro_keys[1], keypresses, "e ", "f ", "g ", "h ", "i ", "j ", "k ", "l ") - FOREACHBIT(report_data.keys[2], keypresses, + FOREACHBIT(report_data.nkro_keys[2], keypresses, "m ", "n ", "o ", "p ", "q ", "r ", "s ", "t ") - FOREACHBIT(report_data.keys[3], keypresses, + FOREACHBIT(report_data.nkro_keys[3], keypresses, "u ", "v ", "w ", "x ", "y ", "z ", "1/! ", "2/@ ") - FOREACHBIT(report_data.keys[4], keypresses, + FOREACHBIT(report_data.nkro_keys[4], keypresses, "3/# ", "4/$ ", "5/% ", "6/^ ", "7/& ", "8/* ", "9/( ", "0/) ") - FOREACHBIT(report_data.keys[5], keypresses, + FOREACHBIT(report_data.nkro_keys[5], keypresses, "enter ", "esc ", "del/bksp ", "tab ", "space ", "-/_ ", "=/+ ", "[/{ ") - FOREACHBIT(report_data.keys[6], keypresses, + FOREACHBIT(report_data.nkro_keys[6], keypresses, "]/} ", "\\/| ", "#/~ ", ";/: ", "'/\" ", "`/~ ", ",/< ", "./> ") - FOREACHBIT(report_data.keys[7], keypresses, + FOREACHBIT(report_data.nkro_keys[7], keypresses, "//? ", "capslock ", "F1 ", "F2 ", "F3 ", "F4 ", "F5 ", "F6 ") - FOREACHBIT(report_data.keys[8], keypresses, + FOREACHBIT(report_data.nkro_keys[8], keypresses, "F7 ", "F8 ", "F9 ", "F10 ", "F11 ", "F12 ", "prtscr ", "scrolllock ") - FOREACHBIT(report_data.keys[9], keypresses, + FOREACHBIT(report_data.nkro_keys[9], keypresses, "pause ", "ins ", "home ", "pgup ", "del ", "end ", "pgdn ", "r_arrow ") - FOREACHBIT(report_data.keys[10], keypresses, + FOREACHBIT(report_data.nkro_keys[10], keypresses, "l_arrow ", "d_arrow ", "u_arrow ", "numlock ", "num/ ", "num* ", "num- ", "num+ ") - FOREACHBIT(report_data.keys[11], keypresses, + FOREACHBIT(report_data.nkro_keys[11], keypresses, "numenter ", "num1 ", "num2 ", "num3 ", "num4 ", "num5 ", "num6 ", "num7 ") - FOREACHBIT(report_data.keys[12], keypresses, + FOREACHBIT(report_data.nkro_keys[12], keypresses, "num8 ", "num9 ", "num0 ", "num. ", "\\/| ", "app ", "power ", "num= ") - FOREACHBIT(report_data.keys[13], keypresses, + FOREACHBIT(report_data.nkro_keys[13], keypresses, "F13 ", "F14 ", "F15 ", "F16 ", "F17 ", "F18 ", "F19 ", "F20 ") - FOREACHBIT(report_data.keys[14], keypresses, + FOREACHBIT(report_data.nkro_keys[14], keypresses, "F21 ", "F22 ", "F23 ", "F24 ", "exec ", "help ", "menu ", "sel ") - FOREACHBIT(report_data.keys[15], keypresses, + FOREACHBIT(report_data.nkro_keys[15], keypresses, "stop ", "again ", "undo ", "cut ", "copy ", "paste ", "find ", "mute ") - FOREACHBIT(report_data.keys[16], keypresses, + FOREACHBIT(report_data.nkro_keys[16], keypresses, "volup ", "voldn ", "capslock_l ", "numlock_l ", "scrolllock_l ", "num, ", "num= ", "(other) ") // clang-format on - for (int i = 17; i < KEY_BYTES; i++) { + for (int i = 17; i < NKRO_KEY_BYTES; i++) { // A little imprecise, in two ways: // (1) obviously, "(other)" refers to many distinct keys // (2) this might undercount the number of "other" keys pressed // Therefore, if any keys are frequently used, they should be handled above and not via "other" - if (report_data.keys[i]) keypresses << "(other) "; + if (report_data.nkro_keys[i]) keypresses << "(other) "; } } diff --git a/src/kaleidoscope/driver/hid/base/Keyboard.h b/src/kaleidoscope/driver/hid/base/Keyboard.h index 47dcf4e93a..661994c76a 100644 --- a/src/kaleidoscope/driver/hid/base/Keyboard.h +++ b/src/kaleidoscope/driver/hid/base/Keyboard.h @@ -38,8 +38,11 @@ class NoBootKeyboard { uint8_t getProtocol() { return 1; } - void setProtocol(uint8_t protocol) {} - void setDefaultProtocol(uint8_t protocol) {} + + uint8_t getBootOnly() { + return 0; + } + void setBootOnly(uint8_t bootonly) {} void sendReport() {} @@ -70,38 +73,6 @@ class NoBootKeyboard { void onUSBReset() {} }; -class NoNKROKeyboard { - public: - NoNKROKeyboard() {} - void begin() {} - - void sendReport() {} - - void press(uint8_t code) {} - void release(uint8_t code) {} - void releaseAll() {} - - bool isModifierActive(Key key) { - return false; - } - bool wasModifierActive(Key key) { - return false; - } - bool isAnyModifierActive() { - return false; - } - bool wasAnyModifierActive() { - return false; - } - bool isKeyPressed(uint8_t code) { - return false; - } - - uint8_t getLeds() { - return 0; - } -}; - class NoConsumerControl { public: NoConsumerControl() {} @@ -125,7 +96,6 @@ class NoSystemControl { struct KeyboardProps { typedef NoBootKeyboard BootKeyboard; - typedef NoNKROKeyboard NKROKeyboard; typedef NoConsumerControl ConsumerControl; typedef NoSystemControl SystemControl; }; @@ -134,7 +104,6 @@ template class Keyboard { private: typename _Props::BootKeyboard boot_keyboard_; - typename _Props::NKROKeyboard nkro_keyboard_; typename _Props::ConsumerControl consumer_control_; typename _Props::SystemControl system_control_; @@ -143,24 +112,19 @@ class Keyboard { void setup() __attribute__((noinline)) { boot_keyboard_.begin(); - nkro_keyboard_.begin(); consumer_control_.begin(); system_control_.begin(); } void sendReport() __attribute__((noinline)) { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - boot_keyboard_.sendReport(); - return; + boot_keyboard_.sendReport(); + if (boot_keyboard_.getProtocol() != HID_BOOT_PROTOCOL) { + consumer_control_.sendReport(); } - nkro_keyboard_.sendReport(); - consumer_control_.sendReport(); } void releaseAllKeys() __attribute__((noinline)) { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - boot_keyboard_.releaseAll(); - } else { - nkro_keyboard_.releaseAll(); + boot_keyboard_.releaseAll(); + if (boot_keyboard_.getProtocol() != HID_BOOT_PROTOCOL) { consumer_control_.releaseAll(); } } @@ -204,21 +168,11 @@ class Keyboard { // pressRawKey takes a Key object and calles KeyboardioHID's ".press" method // with its keycode. It does no processing of any flags or modifiers on the key void pressRawKey(Key pressed_key) { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - boot_keyboard_.press(pressed_key.getKeyCode()); - return; - } - - nkro_keyboard_.press(pressed_key.getKeyCode()); + boot_keyboard_.press(pressed_key.getKeyCode()); } void releaseRawKey(Key released_key) { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - boot_keyboard_.release(released_key.getKeyCode()); - return; - } - - nkro_keyboard_.release(released_key.getKeyCode()); + boot_keyboard_.release(released_key.getKeyCode()); } void releaseKey(Key released_key) { @@ -227,60 +181,38 @@ class Keyboard { } bool isKeyPressed(Key key) { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - return boot_keyboard_.isKeyPressed(key.getKeyCode()); - } - return nkro_keyboard_.isKeyPressed(key.getKeyCode()); + return boot_keyboard_.isKeyPressed(key.getKeyCode()); } bool isModifierKeyActive(Key modifier_key) { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - return boot_keyboard_.isModifierActive(modifier_key.getKeyCode()); - } - - return nkro_keyboard_.isModifierActive(modifier_key.getKeyCode()); + return boot_keyboard_.isModifierActive(modifier_key.getKeyCode()); } bool wasModifierKeyActive(Key modifier_key) { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - return boot_keyboard_.wasModifierActive(modifier_key.getKeyCode()); - } - - return nkro_keyboard_.wasModifierActive(modifier_key.getKeyCode()); + return boot_keyboard_.wasModifierActive(modifier_key.getKeyCode()); } bool isAnyModifierKeyActive() { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - return boot_keyboard_.isAnyModifierActive(); - } - - return nkro_keyboard_.isAnyModifierActive(); + return boot_keyboard_.isAnyModifierActive(); } bool wasAnyModifierKeyActive() { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - return boot_keyboard_.wasAnyModifierActive(); - } - - return nkro_keyboard_.wasAnyModifierActive(); + return boot_keyboard_.wasAnyModifierActive(); } uint8_t getKeyboardLEDs() { - if (boot_keyboard_.getProtocol() == HID_BOOT_PROTOCOL) { - return boot_keyboard_.getLeds(); - } - - return nkro_keyboard_.getLeds(); + return boot_keyboard_.getLeds(); } uint8_t getProtocol() { return boot_keyboard_.getProtocol(); } - void setProtocol(uint8_t protocol) { - boot_keyboard_.setProtocol(protocol); + + uint8_t getBootOnly() { + return boot_keyboard_.getBootOnly(); } - void setDefaultProtocol(uint8_t protocol) { - boot_keyboard_.setDefaultProtocol(protocol); + void setBootOnly(uint8_t bootonly) { + boot_keyboard_.setBootOnly(bootonly); } void onUSBReset() { diff --git a/src/kaleidoscope/driver/hid/keyboardio/Keyboard.h b/src/kaleidoscope/driver/hid/keyboardio/Keyboard.h index 5da9b72165..6930f0033f 100644 --- a/src/kaleidoscope/driver/hid/keyboardio/Keyboard.h +++ b/src/kaleidoscope/driver/hid/keyboardio/Keyboard.h @@ -23,6 +23,11 @@ // From Kaleidoscope: #include "kaleidoscope/driver/hid/base/Keyboard.h" // for Keyboard, KeyboardProps +#if !BOOTKB_HYBRID +#error "This version of Kaleidoscope requires KeyboardioHID with BOOTKB_HYBRID" +#error "Please update your KeyboardioHID" +#endif + namespace kaleidoscope { namespace driver { namespace hid { @@ -49,12 +54,12 @@ class BootKeyboardWrapper { uint8_t getProtocol() { return BootKeyboard().getProtocol(); } - void setProtocol(uint8_t protocol) { - BootKeyboard().setProtocol(protocol); + + uint8_t getBootOnly() { + return BootKeyboard().getBootOnly(); } - void setDefaultProtocol(uint8_t protocol) { - BootKeyboard().default_protocol = protocol; - setProtocol(protocol); + void setBootOnly(uint8_t bootonly) { + BootKeyboard().setBootOnly(bootonly); } void sendReport() { @@ -96,48 +101,6 @@ class BootKeyboardWrapper { } }; -class NKROKeyboardWrapper { - public: - NKROKeyboardWrapper() {} - void begin() { - Keyboard.begin(); - } - - void sendReport() { - Keyboard.sendReport(); - } - - void press(uint8_t code) { - Keyboard.press(code); - } - void release(uint8_t code) { - Keyboard.release(code); - } - void releaseAll() { - Keyboard.releaseAll(); - } - - bool isKeyPressed(uint8_t code) { - return Keyboard.isKeyPressed(code); - } - bool isModifierActive(uint8_t code) { - return Keyboard.isModifierActive(code); - } - bool wasModifierActive(uint8_t code) { - return Keyboard.wasModifierActive(code); - } - bool isAnyModifierActive() { - return Keyboard.isAnyModifierActive(); - } - bool wasAnyModifierActive() { - return Keyboard.wasAnyModifierActive(); - } - - uint8_t getLeds() { - return Keyboard.getLEDs(); - } -}; - class ConsumerControlWrapper { public: ConsumerControlWrapper() {} @@ -177,7 +140,6 @@ class SystemControlWrapper { struct KeyboardProps : public base::KeyboardProps { typedef BootKeyboardWrapper BootKeyboard; - typedef NKROKeyboardWrapper NKROKeyboard; typedef ConsumerControlWrapper ConsumerControl; typedef SystemControlWrapper SystemControl; }; diff --git a/testing/HIDState.cpp b/testing/HIDState.cpp index 4aac34484a..2bce0cd0e5 100644 --- a/testing/HIDState.cpp +++ b/testing/HIDState.cpp @@ -76,7 +76,7 @@ void HIDStateBuilder::ProcessHidReport( uint8_t id, const void *data, int len, int result) { switch (id) { case HID_REPORTID_KEYBOARD: { - LOG(ERROR) << "Dropped BootKeyboardReport: unimplemented"; + ProcessKeyboardReport(KeyboardReport{data}); break; } case HID_REPORTID_GAMEPAD: { @@ -100,7 +100,7 @@ void HIDStateBuilder::ProcessHidReport( break; } case HID_REPORTID_NKRO_KEYBOARD: { - ProcessKeyboardReport(KeyboardReport{data}); + LOG(ERROR) << "Dropped NKROKeyboardReport: unimplemented"; break; } default: diff --git a/testing/KeyboardReport.cpp b/testing/KeyboardReport.cpp index 429e0f2682..9ebbfc1ced 100644 --- a/testing/KeyboardReport.cpp +++ b/testing/KeyboardReport.cpp @@ -64,7 +64,7 @@ std::vector KeyboardReport::ActiveNonModifierKeycodes() const { for (uint8_t i = 0; i < HID_LAST_KEY; ++i) { uint8_t bit = 1 << (uint8_t(i) % 8); - uint8_t keycode = report_data_.keys[i / 8] & bit; + uint8_t keycode = report_data_.nkro_keys[i / 8] & bit; if (keycode) active_keycodes.push_back(i); } diff --git a/testing/KeyboardReport.h b/testing/KeyboardReport.h index 47d06e7730..0d4ec29c0c 100644 --- a/testing/KeyboardReport.h +++ b/testing/KeyboardReport.h @@ -19,17 +19,17 @@ #include // for uint8_t, uint32_t #include // for vector -#include "HID-Settings.h" // for HID_REPORTID_NKRO_KEYBOARD -#include "MultiReport/Keyboard.h" // for HID_KeyboardReport_Data_t +#include "HID-Settings.h" // for HID_REPORTID_NKRO_KEYBOARD +#include "BootKeyboard/BootKeyboard.h" // for HID_KeyboardReport_Data_t namespace kaleidoscope { namespace testing { class KeyboardReport { public: - typedef HID_KeyboardReport_Data_t ReportData; + typedef HID_BootKeyboardReport_Data_t ReportData; - static constexpr uint8_t kHidReportType = HID_REPORTID_NKRO_KEYBOARD; + static constexpr uint8_t kHidReportType = HID_REPORTID_KEYBOARD; KeyboardReport(const void *data);