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>
This commit is contained in:
Родитель
a3b3ca753c
Коммит
76460a7bec
|
@ -23,6 +23,7 @@
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/sched.h> /* HZ */
|
#include <linux/sched.h> /* HZ */
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/timekeeping.h>
|
||||||
|
|
||||||
/*#include <asm/io.h>*/
|
/*#include <asm/io.h>*/
|
||||||
|
|
||||||
|
@ -30,6 +31,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||||
MODULE_DESCRIPTION("Generic gameport layer");
|
MODULE_DESCRIPTION("Generic gameport layer");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
static bool use_ktime = true;
|
||||||
|
module_param(use_ktime, bool, 0400);
|
||||||
|
MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* gameport_mutex protects entire gameport subsystem and is taken
|
* gameport_mutex protects entire gameport subsystem and is taken
|
||||||
* every time gameport port or driver registrered or unregistered.
|
* every time gameport port or driver registrered or unregistered.
|
||||||
|
@ -75,6 +80,38 @@ static unsigned int get_time_pit(void)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int gameport_measure_speed(struct gameport *gameport)
|
static int gameport_measure_speed(struct gameport *gameport)
|
||||||
|
{
|
||||||
|
unsigned int i, t, tx;
|
||||||
|
u64 t1, t2, t3;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tx = ~0;
|
||||||
|
|
||||||
|
for (i = 0; i < 50; i++) {
|
||||||
|
local_irq_save(flags);
|
||||||
|
t1 = ktime_get_ns();
|
||||||
|
for (t = 0; t < 50; t++)
|
||||||
|
gameport_read(gameport);
|
||||||
|
t2 = ktime_get_ns();
|
||||||
|
t3 = ktime_get_ns();
|
||||||
|
local_irq_restore(flags);
|
||||||
|
udelay(i * 10);
|
||||||
|
t = (t2 - t1) - (t3 - t2);
|
||||||
|
if (t < tx)
|
||||||
|
tx = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameport_close(gameport);
|
||||||
|
t = 1000000 * 50;
|
||||||
|
if (tx)
|
||||||
|
t /= tx;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int old_gameport_measure_speed(struct gameport *gameport)
|
||||||
{
|
{
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
|
|
||||||
|
@ -521,7 +558,9 @@ static void gameport_add_port(struct gameport *gameport)
|
||||||
if (gameport->parent)
|
if (gameport->parent)
|
||||||
gameport->parent->child = gameport;
|
gameport->parent->child = gameport;
|
||||||
|
|
||||||
gameport->speed = gameport_measure_speed(gameport);
|
gameport->speed = use_ktime ?
|
||||||
|
gameport_measure_speed(gameport) :
|
||||||
|
old_gameport_measure_speed(gameport);
|
||||||
|
|
||||||
list_add_tail(&gameport->node, &gameport_list);
|
list_add_tail(&gameport->node, &gameport_list);
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <linux/gameport.h>
|
#include <linux/gameport.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/timex.h>
|
#include <linux/timex.h>
|
||||||
|
#include <linux/timekeeping.h>
|
||||||
|
|
||||||
#define DRIVER_DESC "Analog joystick and gamepad driver"
|
#define DRIVER_DESC "Analog joystick and gamepad driver"
|
||||||
|
|
||||||
|
@ -43,6 +44,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
static bool use_ktime = true;
|
||||||
|
module_param(use_ktime, bool, 0400);
|
||||||
|
MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Option parsing.
|
* Option parsing.
|
||||||
*/
|
*/
|
||||||
|
@ -171,6 +176,25 @@ static unsigned long analog_faketime = 0;
|
||||||
#warning Precise timer not defined for this architecture.
|
#warning Precise timer not defined for this architecture.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static inline u64 get_time(void)
|
||||||
|
{
|
||||||
|
if (use_ktime) {
|
||||||
|
return ktime_get_ns();
|
||||||
|
} else {
|
||||||
|
unsigned int x;
|
||||||
|
GET_TIME(x);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int delta(u64 x, u64 y)
|
||||||
|
{
|
||||||
|
if (use_ktime)
|
||||||
|
return y - x;
|
||||||
|
else
|
||||||
|
return DELTA((unsigned int)x, (unsigned int)y);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* analog_decode() decodes analog joystick data and reports input events.
|
* analog_decode() decodes analog joystick data and reports input events.
|
||||||
*/
|
*/
|
||||||
|
@ -226,7 +250,8 @@ static void analog_decode(struct analog *analog, int *axes, int *initial, int bu
|
||||||
static int analog_cooked_read(struct analog_port *port)
|
static int analog_cooked_read(struct analog_port *port)
|
||||||
{
|
{
|
||||||
struct gameport *gameport = port->gameport;
|
struct gameport *gameport = port->gameport;
|
||||||
unsigned int time[4], start, loop, now, loopout, timeout;
|
u64 time[4], start, loop, now;
|
||||||
|
unsigned int loopout, timeout;
|
||||||
unsigned char data[4], this, last;
|
unsigned char data[4], this, last;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
@ -236,7 +261,7 @@ static int analog_cooked_read(struct analog_port *port)
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
gameport_trigger(gameport);
|
gameport_trigger(gameport);
|
||||||
GET_TIME(now);
|
now = get_time();
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
|
|
||||||
start = now;
|
start = now;
|
||||||
|
@ -249,16 +274,16 @@ static int analog_cooked_read(struct analog_port *port)
|
||||||
|
|
||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
this = gameport_read(gameport) & port->mask;
|
this = gameport_read(gameport) & port->mask;
|
||||||
GET_TIME(now);
|
now = get_time();
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
|
|
||||||
if ((last ^ this) && (DELTA(loop, now) < loopout)) {
|
if ((last ^ this) && (delta(loop, now) < loopout)) {
|
||||||
data[i] = last ^ this;
|
data[i] = last ^ this;
|
||||||
time[i] = now;
|
time[i] = now;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (this && (i < 4) && (DELTA(start, now) < timeout));
|
} while (this && (i < 4) && (delta(start, now) < timeout));
|
||||||
|
|
||||||
this <<= 4;
|
this <<= 4;
|
||||||
|
|
||||||
|
@ -266,7 +291,7 @@ static int analog_cooked_read(struct analog_port *port)
|
||||||
this |= data[i];
|
this |= data[i];
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++)
|
||||||
if (data[i] & (1 << j))
|
if (data[i] & (1 << j))
|
||||||
port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
|
port->axes[j] = (delta(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -(this != port->mask);
|
return -(this != port->mask);
|
||||||
|
@ -365,31 +390,39 @@ static void analog_close(struct input_dev *dev)
|
||||||
static void analog_calibrate_timer(struct analog_port *port)
|
static void analog_calibrate_timer(struct analog_port *port)
|
||||||
{
|
{
|
||||||
struct gameport *gameport = port->gameport;
|
struct gameport *gameport = port->gameport;
|
||||||
unsigned int i, t, tx, t1, t2, t3;
|
unsigned int i, t, tx;
|
||||||
|
u64 t1, t2, t3;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (use_ktime) {
|
||||||
|
port->speed = 1000000;
|
||||||
|
} else {
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
GET_TIME(t1);
|
t1 = get_time();
|
||||||
#ifdef FAKE_TIME
|
#ifdef FAKE_TIME
|
||||||
analog_faketime += 830;
|
analog_faketime += 830;
|
||||||
#endif
|
#endif
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
GET_TIME(t2);
|
t2 = get_time();
|
||||||
GET_TIME(t3);
|
t3 = get_time();
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
|
|
||||||
port->speed = DELTA(t1, t2) - DELTA(t2, t3);
|
port->speed = delta(t1, t2) - delta(t2, t3);
|
||||||
|
}
|
||||||
|
|
||||||
tx = ~0;
|
tx = ~0;
|
||||||
|
|
||||||
for (i = 0; i < 50; i++) {
|
for (i = 0; i < 50; i++) {
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
GET_TIME(t1);
|
t1 = get_time();
|
||||||
for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); }
|
for (t = 0; t < 50; t++) {
|
||||||
GET_TIME(t3);
|
gameport_read(gameport);
|
||||||
|
t2 = get_time();
|
||||||
|
}
|
||||||
|
t3 = get_time();
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
udelay(i);
|
udelay(i);
|
||||||
t = DELTA(t1, t2) - DELTA(t2, t3);
|
t = delta(t1, t2) - delta(t2, t3);
|
||||||
if (t < tx) tx = t;
|
if (t < tx) tx = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче