diff options
author | Todd Broch <tbroch@chromium.org> | 2014-09-03 19:56:12 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2014-09-03 20:07:05 -0400 |
commit | 017f14e88bf15ca96eb377b3b14fc3c3332e6b9b (patch) | |
tree | 358f0ae1a095964bbb9c4aaadf00b89507534b5e | |
parent | 516d5f8b04ce2bcd24f03323fc743ae25b81373d (diff) |
Input: cros_ec_keyb - optimize ghosting algorithm
Previous algorithm was a bit conservative and complicating with respect to
identifying key ghosting. This CL uses the bitops hamming weight function
(hweight8) to count the number of matching rows for colM & colN. If that
number is > 1 ghosting is present.
Additionally it removes NULL keys and our one virtual keypress KEY_BATTERY
from consideration as these inputs are never physical keypresses.
Signed-off-by: Todd Broch <tbroch@chromium.org>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Reviewed-by: Luigi Semenzato <semenzato@chromium.org>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r-- | drivers/input/keyboard/cros_ec_keyb.c | 92 |
1 files changed, 49 insertions, 43 deletions
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 791781ade4e7..72d3499bb029 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c | |||
@@ -22,6 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/bitops.h> | ||
25 | #include <linux/i2c.h> | 26 | #include <linux/i2c.h> |
26 | #include <linux/input.h> | 27 | #include <linux/input.h> |
27 | #include <linux/interrupt.h> | 28 | #include <linux/interrupt.h> |
@@ -38,6 +39,7 @@ | |||
38 | * @row_shift: log2 or number of rows, rounded up | 39 | * @row_shift: log2 or number of rows, rounded up |
39 | * @keymap_data: Matrix keymap data used to convert to keyscan values | 40 | * @keymap_data: Matrix keymap data used to convert to keyscan values |
40 | * @ghost_filter: true to enable the matrix key-ghosting filter | 41 | * @ghost_filter: true to enable the matrix key-ghosting filter |
42 | * @valid_keys: bitmap of existing keys for each matrix column | ||
41 | * @old_kb_state: bitmap of keys pressed last scan | 43 | * @old_kb_state: bitmap of keys pressed last scan |
42 | * @dev: Device pointer | 44 | * @dev: Device pointer |
43 | * @idev: Input device | 45 | * @idev: Input device |
@@ -49,6 +51,7 @@ struct cros_ec_keyb { | |||
49 | int row_shift; | 51 | int row_shift; |
50 | const struct matrix_keymap_data *keymap_data; | 52 | const struct matrix_keymap_data *keymap_data; |
51 | bool ghost_filter; | 53 | bool ghost_filter; |
54 | uint8_t *valid_keys; | ||
52 | uint8_t *old_kb_state; | 55 | uint8_t *old_kb_state; |
53 | 56 | ||
54 | struct device *dev; | 57 | struct device *dev; |
@@ -57,39 +60,15 @@ struct cros_ec_keyb { | |||
57 | }; | 60 | }; |
58 | 61 | ||
59 | 62 | ||
60 | static bool cros_ec_keyb_row_has_ghosting(struct cros_ec_keyb *ckdev, | ||
61 | uint8_t *buf, int row) | ||
62 | { | ||
63 | int pressed_in_row = 0; | ||
64 | int row_has_teeth = 0; | ||
65 | int col, mask; | ||
66 | |||
67 | mask = 1 << row; | ||
68 | for (col = 0; col < ckdev->cols; col++) { | ||
69 | if (buf[col] & mask) { | ||
70 | pressed_in_row++; | ||
71 | row_has_teeth |= buf[col] & ~mask; | ||
72 | if (pressed_in_row > 1 && row_has_teeth) { | ||
73 | /* ghosting */ | ||
74 | dev_dbg(ckdev->dev, | ||
75 | "ghost found at: r%d c%d, pressed %d, teeth 0x%x\n", | ||
76 | row, col, pressed_in_row, | ||
77 | row_has_teeth); | ||
78 | return true; | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | return false; | ||
84 | } | ||
85 | |||
86 | /* | 63 | /* |
87 | * Returns true when there is at least one combination of pressed keys that | 64 | * Returns true when there is at least one combination of pressed keys that |
88 | * results in ghosting. | 65 | * results in ghosting. |
89 | */ | 66 | */ |
90 | static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) | 67 | static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) |
91 | { | 68 | { |
92 | int row; | 69 | int col1, col2, buf1, buf2; |
70 | struct device *dev = ckdev->dev; | ||
71 | uint8_t *valid_keys = ckdev->valid_keys; | ||
93 | 72 | ||
94 | /* | 73 | /* |
95 | * Ghosting happens if for any pressed key X there are other keys | 74 | * Ghosting happens if for any pressed key X there are other keys |
@@ -103,27 +82,23 @@ static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) | |||
103 | * | 82 | * |
104 | * In this case only X, Y, and Z are pressed, but g appears to be | 83 | * In this case only X, Y, and Z are pressed, but g appears to be |
105 | * pressed too (see Wikipedia). | 84 | * pressed too (see Wikipedia). |
106 | * | ||
107 | * We can detect ghosting in a single pass (*) over the keyboard state | ||
108 | * by maintaining two arrays. pressed_in_row counts how many pressed | ||
109 | * keys we have found in a row. row_has_teeth is true if any of the | ||
110 | * pressed keys for this row has other pressed keys in its column. If | ||
111 | * at any point of the scan we find that a row has multiple pressed | ||
112 | * keys, and at least one of them is at the intersection with a column | ||
113 | * with multiple pressed keys, we're sure there is ghosting. | ||
114 | * Conversely, if there is ghosting, we will detect such situation for | ||
115 | * at least one key during the pass. | ||
116 | * | ||
117 | * (*) This looks linear in the number of keys, but it's not. We can | ||
118 | * cheat because the number of rows is small. | ||
119 | */ | 85 | */ |
120 | for (row = 0; row < ckdev->rows; row++) | 86 | for (col1 = 0; col1 < ckdev->cols; col1++) { |
121 | if (cros_ec_keyb_row_has_ghosting(ckdev, buf, row)) | 87 | buf1 = buf[col1] & valid_keys[col1]; |
122 | return true; | 88 | for (col2 = col1 + 1; col2 < ckdev->cols; col2++) { |
89 | buf2 = buf[col2] & valid_keys[col2]; | ||
90 | if (hweight8(buf1 & buf2) > 1) { | ||
91 | dev_dbg(dev, "ghost found at: B[%02d]:0x%02x & B[%02d]:0x%02x", | ||
92 | col1, buf1, col2, buf2); | ||
93 | return true; | ||
94 | } | ||
95 | } | ||
96 | } | ||
123 | 97 | ||
124 | return false; | 98 | return false; |
125 | } | 99 | } |
126 | 100 | ||
101 | |||
127 | /* | 102 | /* |
128 | * Compares the new keyboard state to the old one and produces key | 103 | * Compares the new keyboard state to the old one and produces key |
129 | * press/release events accordingly. The keyboard state is 13 bytes (one byte | 104 | * press/release events accordingly. The keyboard state is 13 bytes (one byte |
@@ -222,6 +197,30 @@ static void cros_ec_keyb_close(struct input_dev *dev) | |||
222 | free_irq(ec->irq, ckdev); | 197 | free_irq(ec->irq, ckdev); |
223 | } | 198 | } |
224 | 199 | ||
200 | /* | ||
201 | * Walks keycodes flipping bit in buffer COLUMNS deep where bit is ROW. Used by | ||
202 | * ghosting logic to ignore NULL or virtual keys. | ||
203 | */ | ||
204 | static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) | ||
205 | { | ||
206 | int row, col; | ||
207 | int row_shift = ckdev->row_shift; | ||
208 | unsigned short *keymap = ckdev->idev->keycode; | ||
209 | unsigned short code; | ||
210 | |||
211 | BUG_ON(ckdev->idev->keycodesize != sizeof(*keymap)); | ||
212 | |||
213 | for (col = 0; col < ckdev->cols; col++) { | ||
214 | for (row = 0; row < ckdev->rows; row++) { | ||
215 | code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; | ||
216 | if (code && (code != KEY_BATTERY)) | ||
217 | ckdev->valid_keys[col] |= 1 << row; | ||
218 | } | ||
219 | dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", | ||
220 | col, ckdev->valid_keys[col]); | ||
221 | } | ||
222 | } | ||
223 | |||
225 | static int cros_ec_keyb_probe(struct platform_device *pdev) | 224 | static int cros_ec_keyb_probe(struct platform_device *pdev) |
226 | { | 225 | { |
227 | struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); | 226 | struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); |
@@ -242,6 +241,11 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) | |||
242 | &ckdev->cols); | 241 | &ckdev->cols); |
243 | if (err) | 242 | if (err) |
244 | return err; | 243 | return err; |
244 | |||
245 | ckdev->valid_keys = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL); | ||
246 | if (!ckdev->valid_keys) | ||
247 | return -ENOMEM; | ||
248 | |||
245 | ckdev->old_kb_state = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL); | 249 | ckdev->old_kb_state = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL); |
246 | if (!ckdev->old_kb_state) | 250 | if (!ckdev->old_kb_state) |
247 | return -ENOMEM; | 251 | return -ENOMEM; |
@@ -285,6 +289,8 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) | |||
285 | input_set_capability(idev, EV_MSC, MSC_SCAN); | 289 | input_set_capability(idev, EV_MSC, MSC_SCAN); |
286 | input_set_drvdata(idev, ckdev); | 290 | input_set_drvdata(idev, ckdev); |
287 | ckdev->idev = idev; | 291 | ckdev->idev = idev; |
292 | cros_ec_keyb_compute_valid_keys(ckdev); | ||
293 | |||
288 | err = input_register_device(ckdev->idev); | 294 | err = input_register_device(ckdev->idev); |
289 | if (err) { | 295 | if (err) { |
290 | dev_err(dev, "cannot register input device\n"); | 296 | dev_err(dev, "cannot register input device\n"); |