patch-2.3.31 linux/drivers/char/joystick/joy-analog.c
Next file: linux/drivers/char/joystick/joy-analog.h
Previous file: linux/drivers/char/joystick/joy-amiga.c
Back to the patch index
Back to the overall index
- Lines: 290
- Date:
Tue Dec 7 10:13:11 1999
- Orig file:
v2.3.30/linux/drivers/char/joystick/joy-analog.c
- Orig date:
Sun Nov 7 16:37:34 1999
diff -u --recursive --new-file v2.3.30/linux/drivers/char/joystick/joy-analog.c linux/drivers/char/joystick/joy-analog.c
@@ -1,7 +1,9 @@
/*
* joy-analog.c Version 1.2
*
- * Copyright (c) 1996-1998 Vojtech Pavlik
+ * Copyright (c) 1996-1999 Vojtech Pavlik
+ *
+ * Sponsored by SuSE
*/
/*
@@ -30,15 +32,21 @@
*/
#include <asm/io.h>
+#include <asm/param.h>
+#include <asm/system.h>
+#include <linux/config.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/joystick.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/sched.h>
#include <linux/string.h>
+#include <linux/init.h>
-#define JS_AN_MAX_TIME 3000
+#define JS_AN_MAX_TIME 3000 /* 3 ms */
+#define JS_AN_LOOP_TIME 2000 /* 2 t */
static int js_an_port_list[] __initdata = {0x201, 0};
static struct js_port* js_an_port __initdata = NULL;
@@ -46,53 +54,102 @@
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_PARM(js_an, "2-24i");
-static int js_an[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0};
+static int __initdata js_an[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 };
#include "joy-analog.h"
+struct js_ax_info {
+ int io;
+ int speed;
+ int loop;
+ int timeout;
+ struct js_an_info an;
+};
+
+/*
+ * Time macros.
+ */
+
+#ifdef __i386__
+#ifdef CONFIG_X86_TSC
+#define GET_TIME(x) __asm__ __volatile__ ( "rdtsc" : "=a" (x) : : "dx" )
+#define DELTA(x,y) ((x)-(y))
+#define TIME_NAME "TSC"
+#else
+#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0)
+#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0))
+#define TIME_NAME "PIT"
+#endif
+#elif __alpha__
+#define GET_TIME(x) __asm__ __volatile__ ( "rpcc %0" : "=r" (x) )
+#define DELTA(x,y) ((x)-(y))
+#define TIME_NAME "PCC"
+#endif
+
+#ifndef GET_TIME
+#define FAKE_TIME
+static unsigned long js_an_faketime = 0;
+#define GET_TIME(x) do { x = js_an_faketime++; } while(0)
+#define DELTA(x,y) ((x)-(y))
+#define TIME_NAME "Unreliable"
+#endif
+
/*
* js_an_read() reads analog joystick data.
*/
static int js_an_read(void *xinfo, int **axes, int **buttons)
{
- struct js_an_info *info = xinfo;
+ struct js_ax_info *info = xinfo;
+ struct js_an_info *an = &info->an;
+ int io = info->io;
+ unsigned long flags;
unsigned char buf[4];
- int time[4];
- unsigned char u, v, a;
- unsigned int t, t1;
+ unsigned int time[4];
+ unsigned char u, v, w;
+ unsigned int p, q, r, s, t;
int i, j;
- int timeout;
- int io = info->io;
- timeout = (JS_AN_MAX_TIME * js_time_speed_a) >> 10;
-
- info->buttons = (~inb(io) & JS_AN_BUTTONS_STD) >> 4;
+ an->buttons = ~inb(io) >> 4;
i = 0;
- u = a = ((info->mask[0] | info->mask[1]) & JS_AN_AXES_STD) | (info->extensions & JS_AN_HAT_FCS)
- | ((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4);
-
+ w = ((an->mask[0] | an->mask[1]) & JS_AN_AXES_STD) | (an->extensions & JS_AN_HAT_FCS)
+ | ((an->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((an->extensions & JS_AN_BUTTONS_PXY_UV) >> 4);
+ p = info->loop;
+ q = info->timeout;
+
+ __save_flags(flags);
+ __cli();
outb(0xff,io);
- t = js_get_time_a();
+ GET_TIME(r);
+ __restore_flags(flags);
+ t = r;
+ v = w;
do {
- v = inb(io) & a;
- t1 = js_get_time_a();
- if (u ^ v) {
- time[i] = js_delta_a(t1,t);
+ s = t;
+ u = v;
+ __cli();
+ v = inb(io) & w;
+ GET_TIME(t);
+ __restore_flags(flags);
+ if ((u ^ v) && (DELTA(t,s) < p)) {
+ time[i] = t;
buf[i] = u ^ v;
- u = v;
i++;
}
- } while (v && js_delta_a(t1,t) < timeout);
+ } while (v && (i < 4) && (DELTA(t,r) < q));
- for (--i; i >= 0; i--)
+ v <<= 4;
+
+ for (--i; i >= 0; i--) {
+ v |= buf[i];
for (j = 0; j < 4; j++)
- if (buf[i] & (1 << j)) info->axes[j] = (time[i] << 10) / js_time_speed_a;
+ if (buf[i] & (1 << j)) an->axes[j] = (DELTA(time[i],r) << 10) / info->speed;
+ }
- js_an_decode(info, axes, buttons);
+ js_an_decode(an, axes, buttons);
- return 0;
+ return -(v != w);
}
/*
@@ -116,12 +173,53 @@
}
/*
+ * js_an_calibrate_timer() calibrates the timer and computes loop
+ * and timeout values for a joystick port.
+ */
+
+static void __init js_an_calibrate_timer(struct js_ax_info *info)
+{
+ unsigned int i, t, tx, t1, t2, t3;
+ unsigned long flags;
+ int io = info->io;
+
+ save_flags(flags);
+ cli();
+ GET_TIME(t1);
+#ifdef FAKE_TIME
+ js_an_faketime += 830;
+#endif
+ udelay(1000);
+ GET_TIME(t2);
+ GET_TIME(t3);
+ restore_flags(flags);
+
+ info->speed = DELTA(t2, t1) - DELTA(t3, t2);
+
+ tx = 1 << 30;
+
+ for(i = 0; i < 50; i++) {
+ save_flags(flags);
+ cli();
+ GET_TIME(t1);
+ for(t = 0; t < 50; t++) { inb(io); GET_TIME(t2); }
+ GET_TIME(t3);
+ restore_flags(flags);
+ udelay(i);
+ if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t;
+ }
+
+ info->loop = (JS_AN_LOOP_TIME * t) / 50000;
+ info->timeout = (JS_AN_MAX_TIME * info->speed) / 1000;
+}
+
+/*
* js_an_probe() probes for analog joysticks.
*/
static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct js_port *port)
{
- struct js_an_info info;
+ struct js_ax_info info, *ax;
int i, numdev;
unsigned char u;
@@ -129,7 +227,6 @@
if (check_region(io, 1)) return port;
- if (((u = inb(io)) & 3) == 3) return port;
outb(0xff,io);
u = inb(io);
udelay(JS_AN_MAX_TIME);
@@ -138,31 +235,41 @@
if (!u) return port;
if (u & 0xf0) return port;
- if ((numdev = js_an_probe_devs(&info, u, mask0, mask1, port)) <= 0)
+ if ((numdev = js_an_probe_devs(&info.an, u, mask0, mask1, port)) <= 0)
return port;
info.io = io;
+ js_an_calibrate_timer(&info);
+
request_region(info.io, 1, "joystick (analog)");
- port = js_register_port(port, &info, numdev, sizeof(struct js_an_info), js_an_read);
+ port = js_register_port(port, &info, numdev, sizeof(struct js_ax_info), js_an_read);
+ ax = port->info;
for (i = 0; i < numdev; i++)
- printk(KERN_INFO "js%d: %s at %#x\n",
- js_register_device(port, i, js_an_axes(i, &info), js_an_buttons(i, &info),
- js_an_name(i, &info), js_an_open, js_an_close),
- js_an_name(i, &info), info.io);
+ printk(KERN_INFO "js%d: %s at %#x ["TIME_NAME" timer, %d %sHz clock, %d ns res]\n",
+ js_register_device(port, i, js_an_axes(i, &ax->an), js_an_buttons(i, &ax->an),
+ js_an_name(i, &ax->an), js_an_open, js_an_close),
+ js_an_name(i, &ax->an),
+ ax->io,
+ ax->speed > 10000 ? (ax->speed + 800) / 1000 : ax->speed,
+ ax->speed > 10000 ? "M" : "k",
+ ax->loop * 1000000000 / JS_AN_LOOP_TIME / ax->speed);
- js_an_read(port->info, port->axes, port->buttons);
- js_an_init_corr(port->info, port->axes, port->corr, 8);
+ js_an_read(ax, port->axes, port->buttons);
+ js_an_init_corr(&ax->an, port->axes, port->corr, 8);
return port;
}
#ifndef MODULE
-void __init js_an_setup(char *str, int *ints)
+int __init js_an_setup(SETUP_PARAM)
{
int i;
+ SETUP_PARSE(24);
for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1];
+ return 1;
}
+__setup("js_an=", js_an_setup);
#endif
#ifdef MODULE
@@ -193,11 +300,11 @@
void cleanup_module(void)
{
int i;
- struct js_an_info *info;
+ struct js_ax_info *info;
- while (js_an_port != NULL) {
+ while (js_an_port) {
for (i = 0; i < js_an_port->ndevs; i++)
- if (js_an_port->devs[i] != NULL)
+ if (js_an_port->devs[i])
js_unregister_device(js_an_port->devs[i]);
info = js_an_port->info;
release_region(info->io, 1);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)