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

Tweak OS detect, add OS_DETECTION_SINGLE_REPORT #24379

Merged
merged 1 commit into from
Oct 6, 2024
Merged
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
30 changes: 23 additions & 7 deletions docs/features/os_detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,33 @@ The process is done in steps, generating a number of intermediate results until
We therefore resort to debouncing the result until it has been stable for a given amount of milliseconds.
This amount can be configured, in case your board is not stable within the default debouncing time of 200ms.

## KVM and USB switches

Some KVM and USB switches may not trigger the USB controller on the keyboard to fully reset upon switching machines.
If your keyboard does not redetect the OS in this situation, you can force the keyboard to reset when the USB initialization event is detected, forcing the USB controller to be reconfigured.

## Configuration Options

* `#define OS_DETECTION_DEBOUNCE 200`
* `#define OS_DETECTION_DEBOUNCE 250`
* defined the debounce time for OS detection, in milliseconds
* defaults to 250ms
* `#define OS_DETECTION_KEYBOARD_RESET`
* enables the keyboard reset upon a USB device reinitilization, such as switching devices on some KVMs
* enables the keyboard reset upon a USB device reinitilization
* this setting may help with detection issues when switching between devices on some KVMs (see [Troubleshooting](#troubleshooting))
* `#define OS_DETECTION_SINGLE_REPORT`
* allows the report callbacks to be called only once, when the OS detection result is considered stable
* subsequent changes in the detection results, if any, are ignored
* this setting may help with delayed stability issues when switching devices on some KVMs (see [Troubleshooting](#troubleshooting))

## Troubleshooting

Some KVMs and USB switches may cause issues when the OS detection is turned on.
Here is a list of common issues and how to fix them:

* **Problem**: _keyboard won't redetect the OS when switching between machines using a KVM_
* **Explanation**: some KVMs keep the USB controller powered on during the switch and OS
detection happens when the USB device description is being assembled.
* **Solution**: use `OS_DETECTION_KEYBOARD_RESET` to force the keyboard to reset upon switching.
* **Problem**: _keyboard OS detection callback gets invoked even minuted after startup_
* **Explanation**: some OSes, notably macOS on ARM-based Macs, may cause this behavior.
The actual cause is not known at this time.'
* **Solution**: use `OS_DETECTION_SINGLE_REPORT` to suppress repeated callback invocations.


## Debug

Expand Down
54 changes: 33 additions & 21 deletions quantum/os_detection.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ static uint16_t usb_setups[STORED_USB_SETUPS];
#endif

#ifndef OS_DETECTION_DEBOUNCE
# define OS_DETECTION_DEBOUNCE 200
# define OS_DETECTION_DEBOUNCE 250
#endif

// 2s should always be more than enough (otherwise, you may have other issues)
Expand All @@ -59,39 +59,47 @@ struct setups_data_t setups_data = {
};

static volatile os_variant_t detected_os = OS_UNSURE;
static os_variant_t reported_os = OS_UNSURE;
static volatile os_variant_t reported_os = OS_UNSURE;

// we need to be able to report OS_UNSURE if that is the stable result of the guesses
static bool first_report = true;
static volatile bool first_report = true;

// to react on USB state changes
static volatile enum usb_device_state current_usb_device_state = USB_DEVICE_STATE_INIT;
static enum usb_device_state reported_usb_device_state = USB_DEVICE_STATE_INIT;
static volatile enum usb_device_state current_usb_device_state = USB_DEVICE_STATE_NO_INIT;
static volatile enum usb_device_state maxprev_usb_device_state = USB_DEVICE_STATE_NO_INIT;

// the OS detection might be unstable for a while, "debounce" it
static volatile bool debouncing = false;
static volatile fast_timer_t last_time;
static volatile fast_timer_t last_time = 0;

void os_detection_task(void) {
#ifdef OS_DETECTION_KEYBOARD_RESET
// resetting the keyboard on the USB device state change callback results in instability, so delegate that to this task
// only take action if it's been stable at least once, to avoid issues with some KVMs
if (current_usb_device_state <= USB_DEVICE_STATE_INIT && maxprev_usb_device_state >= USB_DEVICE_STATE_CONFIGURED) {
if (debouncing && timer_elapsed_fast(last_time) >= OS_DETECTION_DEBOUNCE) {
soft_reset_keyboard();
}
return;
}
#endif
#ifdef OS_DETECTION_SINGLE_REPORT
if (!first_report) {
return;
}
#endif
if (current_usb_device_state == USB_DEVICE_STATE_CONFIGURED) {
// debouncing goes for both the detected OS as well as the USB state
if (debouncing && timer_elapsed_fast(last_time) >= OS_DETECTION_DEBOUNCE) {
debouncing = false;
reported_usb_device_state = current_usb_device_state;
debouncing = false;
last_time = 0;
if (detected_os != reported_os || first_report) {
first_report = false;
reported_os = detected_os;
process_detected_host_os_kb(detected_os);
}
}
}
#ifdef OS_DETECTION_KEYBOARD_RESET
// resetting the keyboard on the USB device state change callback results in instability, so delegate that to this task
// only take action if it's been stable at least once, to avoid issues with some KVMs
else if (current_usb_device_state == USB_DEVICE_STATE_INIT && reported_usb_device_state != USB_DEVICE_STATE_INIT) {
soft_reset_keyboard();
}
#endif
}

__attribute__((weak)) bool process_detected_host_os_kb(os_variant_t detected_os) {
Expand Down Expand Up @@ -155,16 +163,20 @@ os_variant_t detected_host_os(void) {

void erase_wlength_data(void) {
memset(&setups_data, 0, sizeof(setups_data));
detected_os = OS_UNSURE;
reported_os = OS_UNSURE;
current_usb_device_state = USB_DEVICE_STATE_INIT;
reported_usb_device_state = USB_DEVICE_STATE_INIT;
debouncing = false;
first_report = true;
detected_os = OS_UNSURE;
reported_os = OS_UNSURE;
current_usb_device_state = USB_DEVICE_STATE_NO_INIT;
maxprev_usb_device_state = USB_DEVICE_STATE_NO_INIT;
debouncing = false;
last_time = 0;
first_report = true;
}

void os_detection_notify_usb_device_state_change(enum usb_device_state usb_device_state) {
// treat this like any other source of instability
if (maxprev_usb_device_state < current_usb_device_state) {
maxprev_usb_device_state = current_usb_device_state;
}
current_usb_device_state = usb_device_state;
last_time = timer_read_fast();
debouncing = true;
Expand Down