summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTing Shen <phoenixshen@google.com>2019-12-06 12:59:40 +0800
committerCommit Bot <commit-bot@chromium.org>2019-12-12 11:03:45 +0000
commita56cdc5ff965e9f2f35cfab232022cb367820634 (patch)
tree3845c172df6f051cf2f14a9e50a5cf4c4bdf23cd
parentbf86d97b7b3bf0401b96752d3924e8d3364e2b30 (diff)
downloadchrome-ec-a56cdc5ff965e9f2f35cfab232022cb367820634.tar.gz
keyboard_scan: detect inconsistent state in read_matrix
Current read_matrix() function didn't handle keyboard state change during the scanning loop. For example, consider keys J(c6r4), L(c9r4), H(c6r1) and F9(c9r1), and the following sequence: - User presses and holds J - Key scan task reads col 6, got state[6] = 0x10 (J) - User presses H+J+L, ghost key F9 is also "pressed" at this point. - Key scan task reads col 9, got state[9] = 0x12 (L+F9) - state[6] and state[9] has only one common bit, so it passes has_ghosting check. - EC thinks J+L+F9 clicked. Implemented a simple heuristic to detect this case, and update state[] array to something likely to be the state after the key press. With this change, we will no longer distinguish J -> H+J+L and J->F9+J+L. The latter used to be accepted but it'll be rejected by this change. BUG=b:145405136 TEST=hold J and L, press H repeatedly, make sure F9 never triggered. BRANCH=kukui,hatch TEST=make run-kb_scan (uncomment kb_scan in test/build.mk). TEST=The average wait_time is reduced about 100 usec on Nami: 1746 msec (old) v.s. 1636 msec (new). Change-Id: Ia20d5a283639d291530e5983254f6163f5c3537f Signed-off-by: Ting Shen <phoenixshen@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1955105 Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org> Commit-Queue: Ting Shen <phoenixshen@chromium.org> Tested-by: Ting Shen <phoenixshen@chromium.org>
-rw-r--r--common/keyboard_scan.c65
1 files changed, 48 insertions, 17 deletions
diff --git a/common/keyboard_scan.c b/common/keyboard_scan.c
index b0b182fb47..c19140cf0a 100644
--- a/common/keyboard_scan.c
+++ b/common/keyboard_scan.c
@@ -16,6 +16,7 @@
#include "keyboard_protocol.h"
#include "keyboard_raw.h"
#include "keyboard_scan.h"
+#include "keyboard_test.h"
#include "lid_switch.h"
#include "switch.h"
#include "system.h"
@@ -228,51 +229,81 @@ static void simulate_key(int row, int col, int pressed)
* Used in pre-init, so must not make task-switching-dependent calls; udelay()
* is ok because it's a spin-loop.
*
- * @param state Destination for new state (must be KEYBOARD_COLS_MAX long).
+ * @param state Destination for new state (must be KEYBOARD_COLS_MAX
+ * long).
*
* @return 1 if at least one key is pressed, else zero.
*/
static int read_matrix(uint8_t *state)
{
int c;
- uint8_t r;
int pressed = 0;
+ /* 1. Read input pins */
for (c = 0; c < keyboard_cols; c++) {
/*
- * Stop if scanning becomes disabled. Note, scanning is enabled
- * on boot by default.
+ * Skip if scanning becomes disabled. Clear the state
+ * to make sure we don't mix new and old states in the
+ * same array.
+ *
+ * Note, scanning is enabled on boot by default.
*/
- if (!keyboard_scan_is_enabled())
- break;
+ if (!keyboard_scan_is_enabled()) {
+ state[c] = 0;
+ continue;
+ }
/* Select column, then wait a bit for it to settle */
keyboard_raw_drive_column(c);
udelay(keyscan_config.output_settle_us);
/* Read the row state */
- r = keyboard_raw_read_rows();
+ state[c] = keyboard_raw_read_rows();
+
+ /* Use simulated keyscan sequence instead if testing active */
+ if (IS_ENABLED(CONFIG_KEYBOARD_TEST))
+ state[c] = keyscan_seq_get_scan(c, state[c]);
+ }
+
+ /* 2. Detect transitional ghost */
+ for (c = 0; c < keyboard_cols; c++) {
+ int c2;
+
+ for (c2 = 0; c2 < c; c2++) {
+ /*
+ * If two columns shares at least one key but their
+ * states are different, maybe the state changed between
+ * two "keyboard_raw_read_rows"s. If this happened,
+ * update both columns to the union of them.
+ *
+ * Note that in theory we need to rescan from col 0 if
+ * anything is updated, to make sure the newly added
+ * bits does not introduce more inconsistency.
+ * Let's ignore this rare case for now.
+ */
+ if ((state[c] & state[c2]) && (state[c] != state[c2])) {
+ uint8_t merged = state[c] | state[c2];
+
+ state[c] = state[c2] = merged;
+ }
+ }
+ }
+ /* 3. Fix result */
+ for (c = 0; c < keyboard_cols; c++) {
/* Add in simulated keypresses */
- r |= simulated_key[c];
+ state[c] |= simulated_key[c];
/*
* Keep track of what keys appear to be pressed. Even if they
* don't exist in the matrix, they'll keep triggering
* interrupts, so we can't leave scanning mode.
*/
- pressed |= r;
+ pressed |= state[c];
/* Mask off keys that don't exist on the actual keyboard */
- r &= keyscan_config.actual_key_mask[c];
-
-#ifdef CONFIG_KEYBOARD_TEST
- /* Use simulated keyscan sequence instead if testing active */
- r = keyscan_seq_get_scan(c, r);
-#endif
+ state[c] &= keyscan_config.actual_key_mask[c];
- /* Store the masked state */
- state[c] = r;
}
keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE);