diff options
| author | Takashi Iwai <tiwai@suse.de> | 2014-09-11 13:28:30 -0400 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2014-09-12 02:41:18 -0400 |
| commit | 76460a7becadbda5589057ee8394cbc98717c324 (patch) | |
| tree | 20acc49eb856fa49c321e3616424f166fc5053a7 | |
| parent | a3b3ca753cdc92c7d5f57404afed3115b3b79cc6 (diff) | |
Input: joystick - use ktime for measuring timing
The current codes in gameport and analog joystick drivers for the time
accounting have a long-standing problem when the system is running
with CPU freq; since the timing is measured via TSC or sample counter,
the calculation isn't reliable.
In this patch, as a simple fix, use the standard ktime to measure the
timing. In case where no high resolution timer is available,
use_ktime bool option is provided to both modules. Setting
use_ktime=false switches to the old methods.
Tested-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
| -rw-r--r-- | drivers/input/gameport/gameport.c | 41 | ||||
| -rw-r--r-- | drivers/input/joystick/analog.c | 71 |
2 files changed, 92 insertions, 20 deletions
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 24c41ba7d4e0..e29c04e2aff4 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/workqueue.h> | 23 | #include <linux/workqueue.h> |
| 24 | #include <linux/sched.h> /* HZ */ | 24 | #include <linux/sched.h> /* HZ */ |
| 25 | #include <linux/mutex.h> | 25 | #include <linux/mutex.h> |
| 26 | #include <linux/timekeeping.h> | ||
| 26 | 27 | ||
| 27 | /*#include <asm/io.h>*/ | 28 | /*#include <asm/io.h>*/ |
| 28 | 29 | ||
| @@ -30,6 +31,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | |||
| 30 | MODULE_DESCRIPTION("Generic gameport layer"); | 31 | MODULE_DESCRIPTION("Generic gameport layer"); |
| 31 | MODULE_LICENSE("GPL"); | 32 | MODULE_LICENSE("GPL"); |
| 32 | 33 | ||
| 34 | static bool use_ktime = true; | ||
| 35 | module_param(use_ktime, bool, 0400); | ||
| 36 | MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed"); | ||
| 37 | |||
| 33 | /* | 38 | /* |
| 34 | * gameport_mutex protects entire gameport subsystem and is taken | 39 | * gameport_mutex protects entire gameport subsystem and is taken |
| 35 | * every time gameport port or driver registrered or unregistered. | 40 | * every time gameport port or driver registrered or unregistered. |
| @@ -76,6 +81,38 @@ static unsigned int get_time_pit(void) | |||
| 76 | 81 | ||
| 77 | static int gameport_measure_speed(struct gameport *gameport) | 82 | static int gameport_measure_speed(struct gameport *gameport) |
| 78 | { | 83 | { |
| 84 | unsigned int i, t, tx; | ||
| 85 | u64 t1, t2, t3; | ||
| 86 | unsigned long flags; | ||
| 87 | |||
| 88 | if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) | ||
| 89 | return 0; | ||
| 90 | |||
| 91 | tx = ~0; | ||
| 92 | |||
| 93 | for (i = 0; i < 50; i++) { | ||
| 94 | local_irq_save(flags); | ||
| 95 | t1 = ktime_get_ns(); | ||
| 96 | for (t = 0; t < 50; t++) | ||
| 97 | gameport_read(gameport); | ||
| 98 | t2 = ktime_get_ns(); | ||
| 99 | t3 = ktime_get_ns(); | ||
| 100 | local_irq_restore(flags); | ||
| 101 | udelay(i * 10); | ||
| 102 | t = (t2 - t1) - (t3 - t2); | ||
| 103 | if (t < tx) | ||
| 104 | tx = t; | ||
| 105 | } | ||
| 106 | |||
| 107 | gameport_close(gameport); | ||
| 108 | t = 1000000 * 50; | ||
| 109 | if (tx) | ||
| 110 | t /= tx; | ||
| 111 | return t; | ||
| 112 | } | ||
| 113 | |||
| 114 | static int old_gameport_measure_speed(struct gameport *gameport) | ||
| 115 | { | ||
| 79 | #if defined(__i386__) | 116 | #if defined(__i386__) |
| 80 | 117 | ||
| 81 | unsigned int i, t, t1, t2, t3, tx; | 118 | unsigned int i, t, t1, t2, t3, tx; |
| @@ -521,7 +558,9 @@ static void gameport_add_port(struct gameport *gameport) | |||
| 521 | if (gameport->parent) | 558 | if (gameport->parent) |
| 522 | gameport->parent->child = gameport; | 559 | gameport->parent->child = gameport; |
| 523 | 560 | ||
| 524 | gameport->speed = gameport_measure_speed(gameport); | 561 | gameport->speed = use_ktime ? |
| 562 | gameport_measure_speed(gameport) : | ||
| 563 | old_gameport_measure_speed(gameport); | ||
| 525 | 564 | ||
| 526 | list_add_tail(&gameport->node, &gameport_list); | 565 | list_add_tail(&gameport->node, &gameport_list); |
| 527 | 566 | ||
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index ab0fdcd36e18..4284080e481d 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | #include <linux/gameport.h> | 36 | #include <linux/gameport.h> |
| 37 | #include <linux/jiffies.h> | 37 | #include <linux/jiffies.h> |
| 38 | #include <linux/timex.h> | 38 | #include <linux/timex.h> |
| 39 | #include <linux/timekeeping.h> | ||
| 39 | 40 | ||
| 40 | #define DRIVER_DESC "Analog joystick and gamepad driver" | 41 | #define DRIVER_DESC "Analog joystick and gamepad driver" |
| 41 | 42 | ||
| @@ -43,6 +44,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | |||
| 43 | MODULE_DESCRIPTION(DRIVER_DESC); | 44 | MODULE_DESCRIPTION(DRIVER_DESC); |
| 44 | MODULE_LICENSE("GPL"); | 45 | MODULE_LICENSE("GPL"); |
| 45 | 46 | ||
| 47 | static bool use_ktime = true; | ||
| 48 | module_param(use_ktime, bool, 0400); | ||
| 49 | MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed"); | ||
| 50 | |||
| 46 | /* | 51 | /* |
| 47 | * Option parsing. | 52 | * Option parsing. |
| 48 | */ | 53 | */ |
| @@ -171,6 +176,25 @@ static unsigned long analog_faketime = 0; | |||
| 171 | #warning Precise timer not defined for this architecture. | 176 | #warning Precise timer not defined for this architecture. |
| 172 | #endif | 177 | #endif |
| 173 | 178 | ||
| 179 | static inline u64 get_time(void) | ||
| 180 | { | ||
| 181 | if (use_ktime) { | ||
| 182 | return ktime_get_ns(); | ||
| 183 | } else { | ||
| 184 | unsigned int x; | ||
| 185 | GET_TIME(x); | ||
| 186 | return x; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | static inline unsigned int delta(u64 x, u64 y) | ||
| 191 | { | ||
| 192 | if (use_ktime) | ||
| 193 | return y - x; | ||
| 194 | else | ||
| 195 | return DELTA((unsigned int)x, (unsigned int)y); | ||
| 196 | } | ||
| 197 | |||
| 174 | /* | 198 | /* |
| 175 | * analog_decode() decodes analog joystick data and reports input events. | 199 | * analog_decode() decodes analog joystick data and reports input events. |
| 176 | */ | 200 | */ |
| @@ -226,7 +250,8 @@ static void analog_decode(struct analog *analog, int *axes, int *initial, int bu | |||
| 226 | static int analog_cooked_read(struct analog_port *port) | 250 | static int analog_cooked_read(struct analog_port *port) |
| 227 | { | 251 | { |
| 228 | struct gameport *gameport = port->gameport; | 252 | struct gameport *gameport = port->gameport; |
| 229 | unsigned int time[4], start, loop, now, loopout, timeout; | 253 | u64 time[4], start, loop, now; |
| 254 | unsigned int loopout, timeout; | ||
| 230 | unsigned char data[4], this, last; | 255 | unsigned char data[4], this, last; |
| 231 | unsigned long flags; | 256 | unsigned long flags; |
| 232 | int i, j; | 257 | int i, j; |
| @@ -236,7 +261,7 @@ static int analog_cooked_read(struct analog_port *port) | |||
| 236 | 261 | ||
| 237 | local_irq_save(flags); | 262 | local_irq_save(flags); |
| 238 | gameport_trigger(gameport); | 263 | gameport_trigger(gameport); |
| 239 | GET_TIME(now); | 264 | now = get_time(); |
| 240 | local_irq_restore(flags); | 265 | local_irq_restore(flags); |
| 241 | 266 | ||
| 242 | start = now; | 267 | start = now; |
| @@ -249,16 +274,16 @@ static int analog_cooked_read(struct analog_port *port) | |||
| 249 | 274 | ||
| 250 | local_irq_disable(); | 275 | local_irq_disable(); |
| 251 | this = gameport_read(gameport) & port->mask; | 276 | this = gameport_read(gameport) & port->mask; |
| 252 | GET_TIME(now); | 277 | now = get_time(); |
| 253 | local_irq_restore(flags); | 278 | local_irq_restore(flags); |
| 254 | 279 | ||
| 255 | if ((last ^ this) && (DELTA(loop, now) < loopout)) { | 280 | if ((last ^ this) && (delta(loop, now) < loopout)) { |
| 256 | data[i] = last ^ this; | 281 | data[i] = last ^ this; |
| 257 | time[i] = now; | 282 | time[i] = now; |
| 258 | i++; | 283 | i++; |
| 259 | } | 284 | } |
| 260 | 285 | ||
| 261 | } while (this && (i < 4) && (DELTA(start, now) < timeout)); | 286 | } while (this && (i < 4) && (delta(start, now) < timeout)); |
| 262 | 287 | ||
| 263 | this <<= 4; | 288 | this <<= 4; |
| 264 | 289 | ||
| @@ -266,7 +291,7 @@ static int analog_cooked_read(struct analog_port *port) | |||
| 266 | this |= data[i]; | 291 | this |= data[i]; |
| 267 | for (j = 0; j < 4; j++) | 292 | for (j = 0; j < 4; j++) |
| 268 | if (data[i] & (1 << j)) | 293 | if (data[i] & (1 << j)) |
| 269 | port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; | 294 | port->axes[j] = (delta(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; |
| 270 | } | 295 | } |
| 271 | 296 | ||
| 272 | return -(this != port->mask); | 297 | return -(this != port->mask); |
| @@ -365,31 +390,39 @@ static void analog_close(struct input_dev *dev) | |||
| 365 | static void analog_calibrate_timer(struct analog_port *port) | 390 | static void analog_calibrate_timer(struct analog_port *port) |
| 366 | { | 391 | { |
| 367 | struct gameport *gameport = port->gameport; | 392 | struct gameport *gameport = port->gameport; |
| 368 | unsigned int i, t, tx, t1, t2, t3; | 393 | unsigned int i, t, tx; |
| 394 | u64 t1, t2, t3; | ||
| 369 | unsigned long flags; | 395 | unsigned long flags; |
| 370 | 396 | ||
| 371 | local_irq_save(flags); | 397 | if (use_ktime) { |
| 372 | GET_TIME(t1); | 398 | port->speed = 1000000; |
| 399 | } else { | ||
| 400 | local_irq_save(flags); | ||
| 401 | t1 = get_time(); | ||
| 373 | #ifdef FAKE_TIME | 402 | #ifdef FAKE_TIME |
| 374 | analog_faketime += 830; | 403 | analog_faketime += 830; |
| 375 | #endif | 404 | #endif |
| 376 | mdelay(1); | 405 | mdelay(1); |
| 377 | GET_TIME(t2); | 406 | t2 = get_time(); |
| 378 | GET_TIME(t3); | 407 | t3 = get_time(); |
| 379 | local_irq_restore(flags); | 408 | local_irq_restore(flags); |
| 380 | 409 | ||
| 381 | port->speed = DELTA(t1, t2) - DELTA(t2, t3); | 410 | port->speed = delta(t1, t2) - delta(t2, t3); |
| 411 | } | ||
| 382 | 412 | ||
| 383 | tx = ~0; | 413 | tx = ~0; |
| 384 | 414 | ||
| 385 | for (i = 0; i < 50; i++) { | 415 | for (i = 0; i < 50; i++) { |
| 386 | local_irq_save(flags); | 416 | local_irq_save(flags); |
| 387 | GET_TIME(t1); | 417 | t1 = get_time(); |
| 388 | for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); } | 418 | for (t = 0; t < 50; t++) { |
| 389 | GET_TIME(t3); | 419 | gameport_read(gameport); |
| 420 | t2 = get_time(); | ||
| 421 | } | ||
| 422 | t3 = get_time(); | ||
| 390 | local_irq_restore(flags); | 423 | local_irq_restore(flags); |
| 391 | udelay(i); | 424 | udelay(i); |
| 392 | t = DELTA(t1, t2) - DELTA(t2, t3); | 425 | t = delta(t1, t2) - delta(t2, t3); |
| 393 | if (t < tx) tx = t; | 426 | if (t < tx) tx = t; |
| 394 | } | 427 | } |
| 395 | 428 | ||
