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 | ||