All of lore.kernel.org
 help / color / mirror / Atom feed
From: Takashi Iwai <tiwai@suse.de>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Andreas Mohr <andi@lisas.de>,
	linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	Vojtech Pavlik <vojtech@suse.cz>, Jiri Kosina <jkosina@suse.cz>
Subject: Re: [PATCH 1/2] SOUND: kill gameport bits
Date: Thu, 21 Aug 2014 13:29:03 +0200	[thread overview]
Message-ID: <s5h4mx6rshs.wl-tiwai@suse.de> (raw)
In-Reply-To: <s5htx57hc89.wl-tiwai@suse.de>

At Wed, 20 Aug 2014 09:05:58 +0200,
Takashi Iwai wrote:
> 
> > > > > Also, I'm left wondering why e.g. my Athlon XP system (a very popular
> > > > > choice for longer times) would be affected by Cpufreq...
> > > > > And there are no details on how exactly cpufreq is a problem or how this
> > > > > timing issue could be fixed...
> > > > 
> > > > If you take a look at gameport_measure_speed() in gameport.c you will see that
> > > > it counts cycles for timing, which obviously does not work that well when CPU
> > > > frequency changes.
> > > > 
> > > > The bugs have been opened in bugzilla/reported on lists ages ago but nobody
> > > > stepped up to fix that.
> > > 
> > > Hm, can't we just use the standard ktime for measuring the time diff?
> > 
> > We could use high-res timers, if they are available. Are they available on such
> > old hardware and are they sufficiently fast to provide needed timings? I
> > definitely do not have any hardware to est with.
> 
> The boards aren't necessarily bound with the old hardware.  PCI boards
> run fine on the modern machines if they still have a PCI slot (how
> lucky).  And, the highres timer itself isn't so new...

I did a quick hack and it seems working on my box.
The patch is below.


thanks,

Takashi

-- 8< --
From: Takashi Iwai <tiwai@suse.de>
Subject: [PATCH] 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.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 drivers/input/gameport/gameport.c | 38 ++++++++++++++++++++-
 drivers/input/joystick/analog.c   | 70 ++++++++++++++++++++++++++++-----------
 2 files changed, 88 insertions(+), 20 deletions(-)

diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index 24c41ba7d4e0..48d91f12e397 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -23,6 +23,7 @@
 #include <linux/workqueue.h>
 #include <linux/sched.h>	/* HZ */
 #include <linux/mutex.h>
+#include <linux/timekeeping.h>
 
 /*#include <asm/io.h>*/
 
@@ -30,6 +31,9 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Generic gameport layer");
 MODULE_LICENSE("GPL");
 
+static bool use_ktime = true;
+module_param(use_ktime, bool, 0400);
+
 /*
  * gameport_mutex protects entire gameport subsystem and is taken
  * every time gameport port or driver registrered or unregistered.
@@ -76,6 +80,36 @@ static unsigned int get_time_pit(void)
 
 static int gameport_measure_speed(struct gameport *gameport)
 {
+	unsigned int i, t, tx;
+	u64 t1, t2;
+	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();
+		local_irq_restore(flags);
+		udelay(i * 10);
+		if (t2 - t1 < tx)
+			tx = t2 - t1;
+	}
+
+	gameport_close(gameport);
+	t = 1000000 * 50;
+	if (tx)
+		t /= tx;
+	return t;
+}
+
+static int old_gameport_measure_speed(struct gameport *gameport)
+{
 #if defined(__i386__)
 
 	unsigned int i, t, t1, t2, t3, tx;
@@ -521,7 +555,9 @@ static void gameport_add_port(struct gameport *gameport)
 	if (gameport->parent)
 		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);
 
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index ab0fdcd36e18..723276d40b58 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -36,6 +36,7 @@
 #include <linux/gameport.h>
 #include <linux/jiffies.h>
 #include <linux/timex.h>
+#include <linux/timekeeping.h>
 
 #define DRIVER_DESC	"Analog joystick and gamepad driver"
 
@@ -43,6 +44,9 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
+static bool use_ktime = true;
+module_param(use_ktime, bool, 0400);
+
 /*
  * Option parsing.
  */
@@ -171,6 +175,25 @@ static unsigned long analog_faketime = 0;
 #warning Precise timer not defined for this architecture.
 #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.
  */
@@ -226,7 +249,8 @@ static void analog_decode(struct analog *analog, int *axes, int *initial, int bu
 static int analog_cooked_read(struct analog_port *port)
 {
 	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 long flags;
 	int i, j;
@@ -236,7 +260,7 @@ static int analog_cooked_read(struct analog_port *port)
 
 	local_irq_save(flags);
 	gameport_trigger(gameport);
-	GET_TIME(now);
+	now = get_time();
 	local_irq_restore(flags);
 
 	start = now;
@@ -249,16 +273,16 @@ static int analog_cooked_read(struct analog_port *port)
 
 		local_irq_disable();
 		this = gameport_read(gameport) & port->mask;
-		GET_TIME(now);
+		now = get_time();
 		local_irq_restore(flags);
 
-		if ((last ^ this) && (DELTA(loop, now) < loopout)) {
+		if ((last ^ this) && (delta(loop, now) < loopout)) {
 			data[i] = last ^ this;
 			time[i] = now;
 			i++;
 		}
 
-	} while (this && (i < 4) && (DELTA(start, now) < timeout));
+	} while (this && (i < 4) && (delta(start, now) < timeout));
 
 	this <<= 4;
 
@@ -266,7 +290,7 @@ static int analog_cooked_read(struct analog_port *port)
 		this |= data[i];
 		for (j = 0; j < 4; 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);
@@ -365,31 +389,39 @@ static void analog_close(struct input_dev *dev)
 static void analog_calibrate_timer(struct analog_port *port)
 {
 	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;
 
-	local_irq_save(flags);
-	GET_TIME(t1);
+	if (use_ktime) {
+		port->speed = 1000000;
+	} else {
+		local_irq_save(flags);
+		t1 = get_time();
 #ifdef FAKE_TIME
-	analog_faketime += 830;
+		analog_faketime += 830;
 #endif
-	mdelay(1);
-	GET_TIME(t2);
-	GET_TIME(t3);
-	local_irq_restore(flags);
+		mdelay(1);
+		t2 = get_time();
+		t3 = get_time();
+		local_irq_restore(flags);
 
-	port->speed = DELTA(t1, t2) - DELTA(t2, t3);
+		port->speed = delta(t1, t2) - delta(t2, t3);
+	}
 
 	tx = ~0;
 
 	for (i = 0; i < 50; i++) {
 		local_irq_save(flags);
-		GET_TIME(t1);
-		for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); }
-		GET_TIME(t3);
+		t1 = get_time();
+		for (t = 0; t < 50; t++) {
+			gameport_read(gameport);
+			t2 = get_time();
+		}
+		t3 = get_time();
 		local_irq_restore(flags);
 		udelay(i);
-		t = DELTA(t1, t2) - DELTA(t2, t3);
+		t = delta(t1, t2) - delta(t2, t3);
 		if (t < tx) tx = t;
 	}
 
-- 
2.0.4


  parent reply	other threads:[~2014-08-21 11:29 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-08-20  2:46 [PATCH 1/2] SOUND: kill gameport bits Andreas Mohr
2014-08-20  5:18 ` Dmitry Torokhov
2014-08-20  5:50   ` Andreas Mohr
2014-08-20  6:09   ` Takashi Iwai
2014-08-20  6:31     ` Dmitry Torokhov
2014-08-20  7:05       ` Takashi Iwai
2014-08-20 12:15         ` Takashi Iwai
2014-08-20 14:49           ` Dmitry Torokhov
2014-08-21  7:16             ` Geert Uytterhoeven
2014-08-20 12:29         ` One Thousand Gnomes
2014-08-20 12:53           ` Geert Uytterhoeven
2014-08-21 11:29         ` Takashi Iwai [this message]
2014-08-24  5:07           ` Andreas Mohr
2014-08-25  7:13             ` Takashi Iwai
2014-08-28 20:03               ` Clemens Ladisch
2014-08-28 21:11                 ` Vojtech Pavlik
2014-08-20 14:27     ` Andreas Mohr
2014-08-20 14:48       ` Dmitry Torokhov
2014-08-20  6:39   ` Vojtech Pavlik
2014-08-20 12:20     ` One Thousand Gnomes
  -- strict thread matches above, loose matches on Subject: below --
2014-08-19 16:41 Dmitry Torokhov
2014-08-20  7:33 ` Clemens Ladisch

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=s5h4mx6rshs.wl-tiwai@suse.de \
    --to=tiwai@suse.de \
    --cc=andi@lisas.de \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jkosina@suse.cz \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=vojtech@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.