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

[Core] suspend: suppress wake up keypress #23389

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from

Conversation

KarlK90
Copy link
Member

@KarlK90 KarlK90 commented Apr 1, 2024

Description

Waking the host from suspend is done by pressing any key on the keyboard, the regular key codes assigned to the keys are not important and must not be sent - otherwise they usually end up in password prompts as ghost characters that have to be deleted again. This PR adds suppression for all keys pressed at the time of wake up. Once a key is released it functions as a regular key again.

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Keyboard (addition or update)
  • Keymap/layout/userspace (addition or update)
  • Documentation

Issues Fixed or Closed by This PR

Checklist

  • My code follows the code style of this project: C, Python
  • I have read the PR Checklist document and have made the appropriate changes.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage).

@github-actions github-actions bot added the core label Apr 1, 2024
@KarlK90 KarlK90 requested a review from a team April 1, 2024 12:52
Comment on lines 63 to 80
void process_wakeup_matrix(uint8_t row, uint8_t col, bool pressed) {
if (!pressed) {
wakeup_matrix[row] &= ~((matrix_row_t)1 << col);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

So the wakeup flag gets cleared only when the main loop had seen both the press and the release event for the wakeup key. However, if USB_SUSPEND_WAKEUP_DELAY is defined to a large enough value (and some boards have it defined to 5000), the key will probably be already released when the main loop gets a chance to run again after the wakeup delay; depending on the debouncing algorithm, the wakeup key may not be reported as pressed at that time, therefore the next real press and release events for that key would be eaten by the wakeup key filter.

Not sure what to do about that, apart from moving the filtering and clearing to the matrix_get_row() call in matrix_task() (but then it would run every time instead of only after detecting a change).

Copy link
Member Author

@KarlK90 KarlK90 Apr 1, 2024

Choose a reason for hiding this comment

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

You're right. I have added a update_wakeup_matrix function in 36f3da61f36252f3818114d8bd2e84e355400d97 which is called after the wake up delay and resets only the keys which have been released during the suspend. This should mitigate the issue?

Copy link
Member Author

Choose a reason for hiding this comment

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

@sigprof friendly ping 🙂

Copy link
Member Author

Choose a reason for hiding this comment

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

@sigprof Please have another look. I'm sure this addresses the issue and I want to resolve the conversation if possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

That update_wakeup_matrix() call may mitigate the problem in some cases, but still leaves some possibilities for a wrong behavior (e.g., if the debouncing algorithm still returns the key state as pressed during update_wakeup_matrix(), but reports the key as released after the next matrix_scan() call from matrix_task()).

Doing things in a 100% correct way could be tricky though:

  • Technically the host can suspend the keyboard even while some keys are held pressed (even though the keyboard would immediately send a remote wakeup request, although the host may disable remote wakeup); in this case the pressed state of such keys must not be lost, so that the eventual key release would be handled and won't leave a stuck key or some other stuck state.
  • Generating key release events without the corresponding key press events would be bad too, so just setting bits in matrix_previous for the keys which were pressed during the wakeup won't work — the extra wakeup_matrix array would still be needed.

So the logic should be:

  1. Key not pressed before suspend, and not pressed after wakeup → do nothing (normal case).
  2. Key not pressed before suspend, but pressed after wakeup → set the wakeup_matrix bit to 1 (so that the press and release events would be suppressed).
  3. Key pressed before suspend, but not pressed after wakeup → do nothing (the release event will be generated on the first matrix_task() call after the wakeup).
  4. Key pressed before suspend, and still pressed after wakeup → do nothing (the release event will be generated some time later).

The suggested implementation does not distinguish the cases 2 and 4, and therefore would mishandle the case 4 (even though it would probably be a rare one), and also would mishandle the case 2 which appears as 1 to matrix_task() (because the debouncing algorithm stops reporting the pressed state at just the right time).

A fully correct logic would need to have access to matrix_previous and would handle the cases above as follows:

  1. matrix_previous bit is 0, matrix_current bit is 0 → do nothing (normal case);
  2. matrix_previous bit is 0, matrix_current bit is 1 → set the matrix_previous bit to 1 (this will suppress the press event), set the wakeup_matrix bit to 1 (this will suppress the subsequent release event).
  3. matrix_previous bit is 1, matrix_current bit is 0 → do nothing (matrix_task() will generate the release event if the matrix state stays like that, or won't do anything if the key gets reported as pressed again — so a momentary release of the key may be missed, but there would be no press/release state inconsistencies).
  4. matrix_previous bit is 1, matrix_current bit is 1 → do nothing (the key release at some point in the future needs to be handled normally).
    So the function to update the matrix state after the wakeup would be like this:
void update_matrix_state_after_wakeup(void) {
    for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
        const matrix_row_t current_row = matrix_get_row(row);
        wakeup_matrix[row] |= current_row & ~matrix_previous[row];
        matrix_previous[row] |= current_row;
    }
}

@KarlK90 KarlK90 requested review from sigprof and a team April 1, 2024 16:06
@KarlK90 KarlK90 force-pushed the feature/suppress-wake-up-keypress branch from 73cdf41 to 36f3da6 Compare April 1, 2024 16:10
@lokher
Copy link
Contributor

lokher commented Apr 5, 2024

the regular key codes assigned to the keys are not important and must not be sent

  1. Some Windows PCs do wake up on usb remote wakeup signal, but sleep again shortly if there's no valid input from keyboard and mouse HID usage page, this means all zero bytes keyboard/mouse report and valid consumer page report can't wake the PC up.

  2. The USB_SUSPEND_WAKEUP_DELAY is not flexible, IMO first SOF after remote wake up means PC USB controller is ready and it's the best time start to send the report, thus no report will be lost. The problem is a queue is needed to buffer the report and need to change the logic to restore the usb state from USB_SUSPENDED to USB_ACTIVE

@KarlK90 KarlK90 force-pushed the feature/suppress-wake-up-keypress branch from 36f3da6 to db8e91c Compare April 5, 2024 10:30
@KarlK90
Copy link
Member Author

KarlK90 commented Apr 9, 2024

the regular key codes assigned to the keys are not important and must not be sent

1. Some Windows PCs do wake up on usb remote wakeup signal, but sleep again shortly if there's no valid input from keyboard and mouse HID usage page, this means all zero bytes keyboard/mouse report and valid consumer page report can't wake the PC up.

Thanks for pointing this out. Is this behavior observed with this PR as well? The last time I captured the wake up sequence of multiple non-qmk keyboards (some Cherry) non of them sent a keyboard report after the remote wake-up, so these keyboards should show the same problem in theory.

2. The `USB_SUSPEND_WAKEUP_DELAY` is not flexible, IMO first SOF after remote wake up means PC USB controller is ready and it's the best time start to send the report, thus no report will be lost.

This is done by the USB peripheral automatically as long as there is data ready to be sent in the output FIFOs. But I agree the USB_SUSPEND_WAKEUP_DELAY is more of a bandaid, then a proper solution.

The problem is a queue is needed to buffer the report and need to change the logic to restore the usb state from USB_SUSPENDED to USB_ACTIVE

The buffering of the reports was implemented in #21656.

Copy link

Thank you for your contribution!
This pull request has been automatically marked as stale because it has not had activity in the last 45 days. It will be closed in 30 days if no further activity occurs. Please feel free to give a status update now, or re-open when it's ready.
For maintainers: Please label with bug, awaiting review, breaking_change, in progress, or on hold to prevent the issue from being re-flagged.

@github-actions github-actions bot added the stale Issues or pull requests that have become inactive without resolution. label May 25, 2024
@KarlK90 KarlK90 added awaiting changes awaiting review in progress and removed awaiting changes stale Issues or pull requests that have become inactive without resolution. labels May 29, 2024
@drashna
Copy link
Member

drashna commented Jun 19, 2024

FWIW, I've been using this for a while, though predominantly on a Mac system, without any issues.

@KarlK90 KarlK90 force-pushed the feature/suppress-wake-up-keypress branch from db8e91c to 6807d84 Compare August 11, 2024 11:51
Waking the host from suspend is done by pressing any key on the
keyboard, the regular key codes assigned to the keys are not important
and must not be sent - otherwise they usually end up in password prompts
as ghost characters that have to be deleted again. This commit adds
suppression for all keys pressed at the time of wake up. Once a key is
released it functions as a regular key again.

Signed-off-by: Stefan Kerkmann <[email protected]>
If USB_SUSPEND_WAKEUP_DELAY is set, the keyboard sleeps during wake up -
which can be up to multiple seconds. To not miss any key releases the
wake up matrix is updated with the current state of the matrix - only
resetting the keys that have been released.

Signed-off-by: Stefan Kerkmann <[email protected]>
@KarlK90 KarlK90 force-pushed the feature/suppress-wake-up-keypress branch from 6807d84 to 71521d7 Compare October 12, 2024 17:46
@KarlK90
Copy link
Member Author

KarlK90 commented Oct 12, 2024

FWIW, I've been using this for a while, though predominantly on a Mac system, without any issues.

Great! Thanks for testing.

@drashna drashna requested a review from a team October 27, 2024 03:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants