Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: hgpk - rework spew detection

The old implementation of spew detection simply tracked the overall
position delta of the cursor over every 100 packets. We found that
this causes occasional false positives in spew detection, and also
that the conditions of the spewy packets are perhaps more fixed than
we once thought.

Rework the spew detection to look for packets of specific small
delta, and only recalibrating if the overall movement delta stays
within expected bounds.

Also discard duplicate packets in the advanced mode, which appear
to be very common. If we don't, the spew detection kicks in far
too early. If we get a large spew of duplicates, request a
recalibration straight up.

Based on earlier work by Paul Fox.

Signed-off-by: Daniel Drake <dsd@laptop.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Daniel Drake and committed by
Dmitry Torokhov
c0dc8342 ca94ec43

+108 -37
+96 -36
drivers/input/mouse/hgpk.c
··· 54 54 MODULE_PARM_DESC(jumpy_delay, 55 55 "delay (ms) before recal after jumpiness detected"); 56 56 57 - static int spew_delay = 1000; 57 + static int spew_delay = 1; 58 58 module_param(spew_delay, int, 0644); 59 59 MODULE_PARM_DESC(spew_delay, 60 60 "delay (ms) before recal after packet spew detected"); ··· 117 117 } 118 118 } 119 119 120 + static void hgpk_reset_spew_detection(struct hgpk_data *priv) 121 + { 122 + priv->spew_count = 0; 123 + priv->dupe_count = 0; 124 + priv->x_tally = 0; 125 + priv->y_tally = 0; 126 + priv->spew_flag = NO_SPEW; 127 + } 128 + 129 + static void hgpk_reset_hack_state(struct psmouse *psmouse) 130 + { 131 + struct hgpk_data *priv = psmouse->private; 132 + 133 + priv->abs_x = priv->abs_y = -1; 134 + hgpk_reset_spew_detection(priv); 135 + } 136 + 120 137 /* 121 138 * We have no idea why this particular hardware bug occurs. The touchpad 122 139 * will randomly start spewing packets without anything touching the ··· 159 142 if (l || r) 160 143 return; 161 144 145 + /* don't track spew if the workaround feature has been turned off */ 146 + if (!spew_delay) 147 + return; 148 + 149 + if (abs(x) > 3 || abs(y) > 3) { 150 + /* no spew, or spew ended */ 151 + hgpk_reset_spew_detection(priv); 152 + return; 153 + } 154 + 155 + /* Keep a tally of the overall delta to the cursor position caused by 156 + * the spew */ 162 157 priv->x_tally += x; 163 158 priv->y_tally += y; 164 159 165 - if (++priv->count > 100) { 160 + switch (priv->spew_flag) { 161 + case NO_SPEW: 162 + /* we're not spewing, but this packet might be the start */ 163 + priv->spew_flag = MAYBE_SPEWING; 164 + 165 + /* fall-through */ 166 + 167 + case MAYBE_SPEWING: 168 + priv->spew_count++; 169 + 170 + if (priv->spew_count < SPEW_WATCH_COUNT) 171 + break; 172 + 173 + /* excessive spew detected, request recalibration */ 174 + priv->spew_flag = SPEW_DETECTED; 175 + 176 + /* fall-through */ 177 + 178 + case SPEW_DETECTED: 179 + /* only recalibrate when the overall delta to the cursor 180 + * is really small. if the spew is causing significant cursor 181 + * movement, it is probably a case of the user moving the 182 + * cursor very slowly across the screen. */ 166 183 if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { 167 - hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n", 184 + hgpk_err(psmouse, "packet spew detected (%d,%d)\n", 168 185 priv->x_tally, priv->y_tally); 186 + priv->spew_flag = RECALIBRATING; 169 187 psmouse_queue_work(psmouse, &priv->recalib_wq, 170 188 msecs_to_jiffies(spew_delay)); 171 189 } 172 - /* reset every 100 packets */ 173 - priv->count = 0; 174 - priv->x_tally = 0; 175 - priv->y_tally = 0; 190 + 191 + break; 192 + case RECALIBRATING: 193 + /* we already detected a spew and requested a recalibration, 194 + * just wait for the queue to kick into action. */ 195 + break; 176 196 } 177 197 } 178 198 ··· 321 267 * If this packet says that the finger was removed, reset our position 322 268 * tracking so that we don't erroneously detect a jump on next press. 323 269 */ 324 - if (!down) 325 - priv->abs_x = priv->abs_y = -1; 326 - 327 - /* 328 - * Report position if finger/pen is down, but weed out duplicate 329 - * packets (we get quite a few in this mode, and they mess up our 330 - * jump detection. 331 - */ 332 - if (down && (x != priv->abs_x || y != priv->abs_y)) { 333 - 334 - /* Don't apply hacks in PT mode, it seems reliable */ 335 - if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { 336 - hgpk_jumpy_hack(psmouse, 337 - priv->abs_x - x, priv->abs_y - y); 338 - hgpk_spewing_hack(psmouse, left, right, 339 - priv->abs_x - x, priv->abs_y - y); 340 - } 341 - 342 - input_report_abs(idev, ABS_X, x); 343 - input_report_abs(idev, ABS_Y, y); 344 - priv->abs_x = x; 345 - priv->abs_y = y; 270 + if (!down) { 271 + hgpk_reset_hack_state(priv); 272 + goto done; 346 273 } 347 274 275 + /* 276 + * Weed out duplicate packets (we get quite a few, and they mess up 277 + * our jump detection) 278 + */ 279 + if (x == priv->abs_x && y == priv->abs_y) { 280 + if (++priv->dupe_count > SPEW_WATCH_COUNT) { 281 + if (tpdebug) 282 + hgpk_dbg(psmouse, "hard spew detected\n"); 283 + priv->spew_flag = RECALIBRATING; 284 + psmouse_queue_work(psmouse, &priv->recalib_wq, 285 + msecs_to_jiffies(spew_delay)); 286 + } 287 + goto done; 288 + } 289 + 290 + /* not a duplicate, continue with position reporting */ 291 + priv->dupe_count = 0; 292 + 293 + /* Don't apply hacks in PT mode, it seems reliable */ 294 + if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { 295 + hgpk_jumpy_hack(psmouse, 296 + priv->abs_x - x, priv->abs_y - y); 297 + hgpk_spewing_hack(psmouse, left, right, 298 + priv->abs_x - x, priv->abs_y - y); 299 + } 300 + 301 + input_report_abs(idev, ABS_X, x); 302 + input_report_abs(idev, ABS_Y, y); 303 + priv->abs_x = x; 304 + priv->abs_y = y; 305 + 306 + done: 348 307 input_sync(idev); 349 308 } 350 309 ··· 527 460 default: 528 461 BUG(); 529 462 } 530 - } 531 - 532 - static void hgpk_reset_hack_state(struct psmouse *psmouse) 533 - { 534 - struct hgpk_data *priv = psmouse->private; 535 - 536 - priv->abs_x = priv->abs_y = -1; 537 463 } 538 464 539 465 static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate)
+12 -1
drivers/input/mouse/hgpk.h
··· 16 16 HGPK_MODEL_D = 0x50, /* C1, mass production */ 17 17 }; 18 18 19 + enum hgpk_spew_flag { 20 + NO_SPEW, 21 + MAYBE_SPEWING, 22 + SPEW_DETECTED, 23 + RECALIBRATING, 24 + }; 25 + 26 + #define SPEW_WATCH_COUNT 42 /* at 12ms/packet, this is 1/2 second */ 27 + 19 28 enum hgpk_mode { 20 29 HGPK_MODE_MOUSE, 21 30 HGPK_MODE_GLIDESENSOR, ··· 36 27 struct psmouse *psmouse; 37 28 enum hgpk_mode mode; 38 29 bool powered; 39 - int count, x_tally, y_tally; /* hardware workaround stuff */ 30 + enum hgpk_spew_flag spew_flag; 31 + int spew_count, x_tally, y_tally; /* spew detection */ 40 32 unsigned long recalib_window; 41 33 struct delayed_work recalib_wq; 42 34 int abs_x, abs_y; 35 + int dupe_count; 43 36 }; 44 37 45 38 #define hgpk_dbg(psmouse, format, arg...) \