From a3e35feed5d5d76b0b73ce7aa34afb075fefb3cd Mon Sep 17 00:00:00 2001 From: an_achronism <87213873+an-achronism@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:29:25 +0000 Subject: [PATCH 1/6] Refactor IBM PC lock state LED update routine Keyboard lock state indicator LEDs are updated by sending a control byte to the keyboard, which has one bit per individual LED state (the rest of the byte being 0s). The order of the bits is different between the USB HID and IBM PC/AT protocols, so they have to be reorganised before TMK sends them to the keyboard. The existing implementation of this works perfectly fine, but reorders the LED state control byte to IBM format before sending it to a single function that sends both the LED control enquiry byte (0xED) and the actual LED state control byte itself, one after the other. However, not every keyboard will respond to the enquiry byte with an acknowledgement (0xFA), e.g. IBM terminal keyboards which will respond instead with an 0xFE meaning "RESEND", in which case the LED state control byte will not be sent, meaning that it was just reorganised unnecessarily. This refactored implementation splits the sending of the enquiry byte into a separate function, so that the enquiry byte is sent first, and the control byte is only reorganised if the keyboard has already responded with the "ACK" acknowledgement byte. --- converter/ibmpc_usb/ibmpc_usb.cpp | 63 ++++++++++++++++++++----------- tmk_core/protocol/ibmpc.cpp | 13 ++++--- tmk_core/protocol/ibmpc.hpp | 1 + 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/converter/ibmpc_usb/ibmpc_usb.cpp b/converter/ibmpc_usb/ibmpc_usb.cpp index 247d556c7d..a7a5ad0432 100644 --- a/converter/ibmpc_usb/ibmpc_usb.cpp +++ b/converter/ibmpc_usb/ibmpc_usb.cpp @@ -96,28 +96,49 @@ uint8_t ibmpc_mouse_buttons(void) void IBMPCConverter::set_led(uint8_t usb_led) { - // Sending before keyboard recognition may be harmful for XT keyboard - if (keyboard_kind == NONE) return; - - // XT keyobard doesn't support any command and it is harmful perhaps - // https://github.com/tmk/tmk_keyboard/issues/635#issuecomment-626993437 - if (keyboard_kind == PC_XT) return; - if (keyboard_kind == PC_MOUSE) return; - - // It should be safe to send the command to keyboards with AT protocol - // - IBM Terminal doesn't support the command and response with 0xFE but it is not harmful. - // - Some other Terminals like G80-2551 supports the command. - // https://geekhack.org/index.php?topic=103648.msg2894921#msg2894921 - + /* Pointing devices and original IBM PC ("XT") protocol keyboards do not + * support receiving data signals, only sending them, so it's probably not a + * good idea to send any to them: + * https://github.com/tmk/tmk_keyboard/issues/635#issuecomment-626993437 + * + * IBM terminal boards don't have LEDs, but are capable of receiving and + * responding to incoming data. This is useful, because it means we can send + * the enquiry byte to terminal keyboards, which will facilitate updating + * the lock state LEDs on terminal keyboards that do have them, like the + * Cherry G80-2551: + * https://geekhack.org/index.php?topic=103648.msg2894921#msg2894921 + * + * The USB HID and IBM PC/AT protocols both set keyboards' lock state LEDs + * by sending a single byte in which each lock state is represented by one + * bit, but the bit order differs between protocols (see led.h and ibmpc.h). + */ + switch (keyboard_kind) { + /* if the connected device is unidentified (temporarily or otherwise), a + * pointing device, or an "XT" protocol keyboard, do nothing: */ + case NONE: + case PC_MOUSE: + case PC_XT: + break; + /* if keyboard returns "ACK" acknowledgement byte (0xFA) when LED update + * enquiry byte (0xED) is sent, reorganise USB HID LED byte into IBM bit + * order and send to keyboard to update all 3 of its lock state LEDs: */ + default: + if (ibmpc.host_enq_led()) { + uint8_t ibm_led = 0; + if (usb_led & (1 << USB_LED_SCROLL_LOCK)) { + ibm_led |= (1 << IBMPC_LED_SCROLL_LOCK); + } + if (usb_led & (1 << USB_LED_NUM_LOCK)) { + ibm_led |= (1 << IBMPC_LED_NUM_LOCK); + } + if (usb_led & (1 << USB_LED_CAPS_LOCK)) { + ibm_led |= (1 << IBMPC_LED_CAPS_LOCK); + } + ibmpc.host_set_led(ibm_led); + } + break; // TODO: PC_TERMINAL_IBM_RT support - uint8_t ibmpc_led = 0; - if (usb_led & (1< Date: Fri, 3 Mar 2023 21:34:46 +0000 Subject: [PATCH 2/6] Disable Mousekeys to prevent compiler crash Even with this change, avr-gcc 8 and 9 are still unable to compile, but it allows for a successful compilation in avr-gcc 5.4 and 5.5. 5.4 is still hanging on for dear life inside the VirtualBox VM provided in the TMK wiki: https://github.com/tmk/tmk_keyboard/wiki/Build-on-VirtualBox 5.5 is still currently available through Homebrew keg here: https://github.com/osx-cross/homebrew-avr I have not yet tested avr-gcc versions 6 or 7, because neither is straightforwardly available to me (i.e. through on a package manager on macOS, Windows, or Linux distro that can be set up quickly on a VM). --- converter/ibmpc_usb/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/converter/ibmpc_usb/Makefile b/converter/ibmpc_usb/Makefile index 96de9cea44..44edaf2b39 100644 --- a/converter/ibmpc_usb/Makefile +++ b/converter/ibmpc_usb/Makefile @@ -71,7 +71,7 @@ OPT_DEFS += -DSUSPEND_MODE_STANDBY # comment out to disable the options. # BOOTMAGIC_ENABLE ?= no # Virtual DIP switch configuration(+1150) -MOUSEKEY_ENABLE ?= yes # Mouse keys(+2200) +MOUSEKEY_ENABLE ?= no # Mouse keys(+2200) EXTRAKEY_ENABLE ?= yes # Audio control and System control(+400) CONSOLE_ENABLE ?= yes # Console for debug(+4150) COMMAND_ENABLE ?= yes # Commands for debug and configuration(+3600) From f14e81c438248e86e09ba10bf5e122f7c671a79a Mon Sep 17 00:00:00 2001 From: an_achronism <87213873+an-achronism@users.noreply.github.com> Date: Sat, 4 Mar 2023 21:22:29 +0000 Subject: [PATCH 3/6] Minor reorganising (e.g. renamed host_enq_led) --- converter/ibmpc_usb/ibmpc_usb.cpp | 12 ++++++------ tmk_core/protocol/ibmpc.cpp | 2 +- tmk_core/protocol/ibmpc.hpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/converter/ibmpc_usb/ibmpc_usb.cpp b/converter/ibmpc_usb/ibmpc_usb.cpp index a7a5ad0432..c405065835 100644 --- a/converter/ibmpc_usb/ibmpc_usb.cpp +++ b/converter/ibmpc_usb/ibmpc_usb.cpp @@ -113,17 +113,17 @@ void IBMPCConverter::set_led(uint8_t usb_led) * bit, but the bit order differs between protocols (see led.h and ibmpc.h). */ switch (keyboard_kind) { - /* if the connected device is unidentified (temporarily or otherwise), a - * pointing device, or an "XT" protocol keyboard, do nothing: */ + /* If the connected device is unidentified (temporarily or otherwise), a + * pointing device, or an "XT" protocol keyboard, do nothing */ case NONE: - case PC_MOUSE: case PC_XT: + case PC_MOUSE: break; - /* if keyboard returns "ACK" acknowledgement byte (0xFA) when LED update + /* If keyboard returns "ACK" acknowledgement byte (0xFA) when LED update * enquiry byte (0xED) is sent, reorganise USB HID LED byte into IBM bit - * order and send to keyboard to update all 3 of its lock state LEDs: */ + * order and send to keyboard to update all 3 of its lock state LEDs */ default: - if (ibmpc.host_enq_led()) { + if (ibmpc.host_led_enq()) { uint8_t ibm_led = 0; if (usb_led & (1 << USB_LED_SCROLL_LOCK)) { ibm_led |= (1 << IBMPC_LED_SCROLL_LOCK); diff --git a/tmk_core/protocol/ibmpc.cpp b/tmk_core/protocol/ibmpc.cpp index b2702cd4a0..b6a3c8355b 100644 --- a/tmk_core/protocol/ibmpc.cpp +++ b/tmk_core/protocol/ibmpc.cpp @@ -402,7 +402,7 @@ void IBMPC::isr(void) } /* send enquiry byte to keyboard to check if it can handle LED state byte */ -bool IBMPC::host_enq_led(void) +bool IBMPC::host_led_enq(void) { return (host_send(IBMPC_SET_LED) == IBMPC_ACK); } diff --git a/tmk_core/protocol/ibmpc.hpp b/tmk_core/protocol/ibmpc.hpp index 65f39dfa9b..a84c8bb37a 100644 --- a/tmk_core/protocol/ibmpc.hpp +++ b/tmk_core/protocol/ibmpc.hpp @@ -110,7 +110,7 @@ class IBMPC int16_t host_recv_response(void); int16_t host_recv(void); void host_isr_clear(void); - bool host_enq_led(void); + bool host_led_enq(void); void host_set_led(uint8_t led); IBMPC(uint8_t clock, uint8_t data) : From 1dd6411dc8e643d7a57178baefd6c1de7c223fff Mon Sep 17 00:00:00 2001 From: an_achronism <87213873+an-achronism@users.noreply.github.com> Date: Sat, 4 Mar 2023 23:45:16 +0000 Subject: [PATCH 4/6] NEW FEATURE: ibmpc_usb converter lock state LEDs Further restructured lock state LED routine to update both the keyboard and any indicators wired to the converter itself. Tested on Unicomp New Model M and various different IBM keyboards (a 5150 one, a 122-key, and a 1990 Enhanced Keyboard). Current behaviour is that the converter LEDs always light regardless of whether the keyboard has them, which is primarily because of edge cases where a keyboard might respond indicating that it can be sent LED state control bytes even though it doesn't actually have LEDs with which to render the information. --- converter/ibmpc_usb/ibmpc_usb.cpp | 43 +++++++++++++++++++++++++++++++ converter/ibmpc_usb/ibmpc_usb.hpp | 2 ++ converter/ibmpc_usb/led_pins.h | 33 ++++++++++++++++++++++++ tmk_core/protocol/ibmpc.cpp | 4 +-- 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 converter/ibmpc_usb/led_pins.h diff --git a/converter/ibmpc_usb/ibmpc_usb.cpp b/converter/ibmpc_usb/ibmpc_usb.cpp index c405065835..f28ec694c0 100644 --- a/converter/ibmpc_usb/ibmpc_usb.cpp +++ b/converter/ibmpc_usb/ibmpc_usb.cpp @@ -29,6 +29,7 @@ along with this program. If not, see . #include "hook.h" #include "ibmpc.hpp" #include "ibmpc_usb.hpp" +#include "led_pins.h" // Converter @@ -71,8 +72,50 @@ uint8_t matrix_scan(void) #endif } +void write_led_pin(uint8_t usb_led, uint8_t pin) +{ + uint8_t relevant_bit; + switch (pin) { +#ifdef CAPS_LOCK_PIN + case CAPS_LOCK_PIN: + relevant_bit = USB_LED_CAPS_LOCK; + break; +#endif +#ifdef NUM_LOCK_PIN + case NUM_LOCK_PIN: + relevant_bit = USB_LED_NUM_LOCK; + break; +#endif +#ifdef SCROLL_LOCK_PIN + case SCROLL_LOCK_PIN: + relevant_bit = USB_LED_SCROLL_LOCK; + break; +#endif + default: + return; + } + if (usb_led & (1 << relevant_bit)) { + LOCK_INDICATOR_DDR |= (1 << pin); + LOCK_INDICATOR_PORT |= (1 << pin); + } else { + LOCK_INDICATOR_DDR &= ~(1 << pin); + LOCK_INDICATOR_PORT &= ~(1 << pin); + } +} + void led_set(uint8_t usb_led) { + // Write lock states to indicators on the converter itself +#ifdef NUM_LOCK_PIN + converter_set_led(usb_led, NUM_LOCK_PIN); +#endif +#ifdef CAPS_LOCK_PIN + converter_set_led(usb_led, CAPS_LOCK_PIN); +#endif +#ifdef SCROLL_LOCK_PIN + converter_set_led(usb_led, SCROLL_LOCK_PIN); +#endif + // Send lock states out to connected input device(s), if appropriate converter0.set_led(usb_led); #if defined(IBMPC_CLOCK_BIT1) && defined(IBMPC_DATA_BIT1) converter1.set_led(usb_led); diff --git a/converter/ibmpc_usb/ibmpc_usb.hpp b/converter/ibmpc_usb/ibmpc_usb.hpp index 068f694cb0..5d262b20e2 100644 --- a/converter/ibmpc_usb/ibmpc_usb.hpp +++ b/converter/ibmpc_usb/ibmpc_usb.hpp @@ -52,6 +52,8 @@ class IBMPCConverter { uint8_t process_interface(void); + void write_led_pin(uint8_t usb_led, uint8_t pin); + void set_led(uint8_t usb_led); static inline void matrix_clear(void) { diff --git a/converter/ibmpc_usb/led_pins.h b/converter/ibmpc_usb/led_pins.h new file mode 100644 index 0000000000..7a179ea0dd --- /dev/null +++ b/converter/ibmpc_usb/led_pins.h @@ -0,0 +1,33 @@ +/* +Copyright 2023 an_achronism <87213873+an-achronism@users.noreply.github.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +/* +The default pin assignments in this file have been chosen to retain full +backwards compatibility with Soarer's old converter solution: +https://geekhack.org/index.php?topic=17458.0 +*/ + +// The Data Direction Register and port must match (default is F) +#define LOCK_INDICATOR_DDR DDRF +#define LOCK_INDICATOR_PORT PORTF + +// These pin numbers correspond with the chosen port, e.g. port F pin 5 is PF5 +#define CAPS_LOCK_PIN 5 +#define NUM_LOCK_PIN 6 +#define SCROLL_LOCK_PIN 7 diff --git a/tmk_core/protocol/ibmpc.cpp b/tmk_core/protocol/ibmpc.cpp index b6a3c8355b..269e5b3ee3 100644 --- a/tmk_core/protocol/ibmpc.cpp +++ b/tmk_core/protocol/ibmpc.cpp @@ -401,13 +401,13 @@ void IBMPC::isr(void) return; } -/* send enquiry byte to keyboard to check if it can handle LED state byte */ +/* Send enquiry byte to keyboard to check if it can handle LED state byte */ bool IBMPC::host_led_enq(void) { return (host_send(IBMPC_SET_LED) == IBMPC_ACK); } -/* send LED state byte to keyboard */ +/* Send LED state byte to keyboard */ void IBMPC::host_set_led(uint8_t led) { host_send(led); From 3b9e8fcdd8d9a6a9f9e036de33778eb00a8881f1 Mon Sep 17 00:00:00 2001 From: an_achronism <87213873+an-achronism@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:39:34 +0000 Subject: [PATCH 5/6] Correcting naming of function call Renamed in one file and not the other, which was an artefact of naming difference in another firmware. Fixed and retested now. Sorry! --- converter/ibmpc_usb/ibmpc_usb.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/converter/ibmpc_usb/ibmpc_usb.cpp b/converter/ibmpc_usb/ibmpc_usb.cpp index f28ec694c0..054078e066 100644 --- a/converter/ibmpc_usb/ibmpc_usb.cpp +++ b/converter/ibmpc_usb/ibmpc_usb.cpp @@ -107,13 +107,13 @@ void led_set(uint8_t usb_led) { // Write lock states to indicators on the converter itself #ifdef NUM_LOCK_PIN - converter_set_led(usb_led, NUM_LOCK_PIN); + write_led_pin(usb_led, NUM_LOCK_PIN); #endif #ifdef CAPS_LOCK_PIN - converter_set_led(usb_led, CAPS_LOCK_PIN); + write_led_pin(usb_led, CAPS_LOCK_PIN); #endif #ifdef SCROLL_LOCK_PIN - converter_set_led(usb_led, SCROLL_LOCK_PIN); + write_led_pin(usb_led, SCROLL_LOCK_PIN); #endif // Send lock states out to connected input device(s), if appropriate converter0.set_led(usb_led); From b57e6493b5ca59cbb5ac43262207d603534407a1 Mon Sep 17 00:00:00 2001 From: an_achronism <87213873+an-achronism@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:45:09 +0000 Subject: [PATCH 6/6] Refactored in response to feedback re. separation My intention with splitting up ibmpc.host_set_led() into two functions was to ensure that the bit order of the USB HID LED update byte was not reconfigured to IBM order unless it was actually going to be used. However, it has been suggested that splitting the check for whether the keyboard can receive the LED byte and the subsequent sending of the LED byte into separate functions in some way implies that they are less contextually connected and inter-reliant, which could hypothetically lead to a misunderstanding and potentially the addition of code that does unwise things with these newly separate functions (e.g. you can't send the LED byte without sending the check byte immediately prior, you can't send multiple LED bytes after only ascertaining once that the keyboard is capable of receiving them, and so forth). To reduce the likelihood of this interpretation, I've removed the case- specific functions for sending the two bytes from ibmpc and called ibmpc.host_send() directly from ibmpc_usb.cpp to make it clearer that the LED byte should only be sent immediately after the check byte. --- converter/ibmpc_usb/ibmpc_usb.cpp | 4 ++-- tmk_core/protocol/ibmpc.cpp | 12 ------------ tmk_core/protocol/ibmpc.hpp | 2 -- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/converter/ibmpc_usb/ibmpc_usb.cpp b/converter/ibmpc_usb/ibmpc_usb.cpp index 054078e066..b559c491f9 100644 --- a/converter/ibmpc_usb/ibmpc_usb.cpp +++ b/converter/ibmpc_usb/ibmpc_usb.cpp @@ -166,7 +166,7 @@ void IBMPCConverter::set_led(uint8_t usb_led) * enquiry byte (0xED) is sent, reorganise USB HID LED byte into IBM bit * order and send to keyboard to update all 3 of its lock state LEDs */ default: - if (ibmpc.host_led_enq()) { + if (ibmpc.host_send(IBMPC_SET_LED) == IBMPC_ACK) { uint8_t ibm_led = 0; if (usb_led & (1 << USB_LED_SCROLL_LOCK)) { ibm_led |= (1 << IBMPC_LED_SCROLL_LOCK); @@ -177,7 +177,7 @@ void IBMPCConverter::set_led(uint8_t usb_led) if (usb_led & (1 << USB_LED_CAPS_LOCK)) { ibm_led |= (1 << IBMPC_LED_CAPS_LOCK); } - ibmpc.host_set_led(ibm_led); + ibmpc.host_send(ibm_led); } break; // TODO: PC_TERMINAL_IBM_RT support diff --git a/tmk_core/protocol/ibmpc.cpp b/tmk_core/protocol/ibmpc.cpp index 269e5b3ee3..3f899745d4 100644 --- a/tmk_core/protocol/ibmpc.cpp +++ b/tmk_core/protocol/ibmpc.cpp @@ -401,18 +401,6 @@ void IBMPC::isr(void) return; } -/* Send enquiry byte to keyboard to check if it can handle LED state byte */ -bool IBMPC::host_led_enq(void) -{ - return (host_send(IBMPC_SET_LED) == IBMPC_ACK); -} - -/* Send LED state byte to keyboard */ -void IBMPC::host_set_led(uint8_t led) -{ - host_send(led); -} - // NOTE: With this ISR data line should be read within 5us after clock falling edge. // Confirmed that ATmega32u4 can read data line in 2.5us from interrupt after // ISR prologue pushs r18, r19, r20, r21, r24, r25 r30 and r31 with GCC 5.4.0 diff --git a/tmk_core/protocol/ibmpc.hpp b/tmk_core/protocol/ibmpc.hpp index a84c8bb37a..3d0b06d241 100644 --- a/tmk_core/protocol/ibmpc.hpp +++ b/tmk_core/protocol/ibmpc.hpp @@ -110,8 +110,6 @@ class IBMPC int16_t host_recv_response(void); int16_t host_recv(void); void host_isr_clear(void); - bool host_led_enq(void); - void host_set_led(uint8_t led); IBMPC(uint8_t clock, uint8_t data) : isr_debug(IBMPC_ERR_NONE), protocol(IBMPC_PROTOCOL_NO), error(IBMPC_ERR_NONE),