summaryrefslogtreecommitdiffstats
path: root/drivers/char/joystick/joy-logitech.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/joystick/joy-logitech.c')
-rw-r--r--drivers/char/joystick/joy-logitech.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/drivers/char/joystick/joy-logitech.c b/drivers/char/joystick/joy-logitech.c
new file mode 100644
index 000000000..2dcafbfeb
--- /dev/null
+++ b/drivers/char/joystick/joy-logitech.c
@@ -0,0 +1,371 @@
+/*
+ * joy-logitech.c Version 1.2
+ *
+ * Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Logitech Digital joystick family.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.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/string.h>
+
+#define JS_LT_MAX_START 250
+#define JS_LT_MAX_STROBE 25
+#define JS_LT_MAX_LENGTH 72
+
+#define JS_LT_MAX_DELAY 12000
+
+#define JS_LT_MODE_WMED 1
+#define JS_LT_MODE_CM2 2
+#define JS_LT_MODE_TPD 3
+
+static int js_lt_seq_init[] __initdata = { 6000, 11000, 7000, 9000, 6000, 11000, 7000, 9000, 0 };
+static int js_lt_seq_reset[] __initdata = { 2000, 3000, 2000, 3000, 0 };
+
+static int js_lt_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_lt_port __initdata = NULL;
+
+static struct {
+ int x;
+ int y;
+} js_lt_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct js_lt_info {
+ int io;
+ unsigned char mode;
+};
+
+/*
+ * js_lt_read_packet() reads a Logitech packet.
+ */
+
+static int js_lt_read_packet(int io, __u64 *data)
+{
+
+ static unsigned char buf[JS_LT_MAX_LENGTH];
+ unsigned char u, v, w, mask = 0;
+ int i;
+ unsigned long flags;
+ unsigned int t, t1;
+
+ int start = (js_time_speed * JS_LT_MAX_START) >> 10;
+ int strobe = (js_time_speed * JS_LT_MAX_STROBE) >> 10;
+
+ u = inb(io) >> 4;
+
+ if (u == 0xc) mask = 0x10;
+ if (u == 0x0) mask = 0x50;
+ if (!mask) return 0;
+
+ i = 0;
+
+ __save_flags(flags);
+ __cli();
+
+ outb(0xff,io);
+
+ u = inb(io);
+ t = js_get_time();
+
+ if ((u & 0xc) != 0xc) mask = 0x10;
+
+ do {
+ u = inb(io);
+ t1 = js_get_time();
+ } while ((((u >> 1) ^ u) & mask) != mask && js_delta(t1,t) < start);
+
+ t = t1;
+
+ do {
+ v = inb(io);
+ t1 = js_get_time();
+ w = u ^ v;
+ if ((((w >> 1) ^ w) & mask) == mask) {
+ buf[i++] = w;
+ t = t1;
+ u = v;
+ }
+ } while (i < JS_LT_MAX_LENGTH && js_delta(t1,t) < strobe);
+
+ __restore_flags(flags);
+
+ t = i;
+ *data = 0;
+
+ if (mask == 0x10) {
+ for (i = 0; i < t; i++)
+ *data = ((buf[i] >> 5) & 1) | (*data << 1);
+ return t;
+ }
+ if (mask == 0x50) {
+ for (i = 0; i < t; i++)
+ *data = ((__u64)(buf[i] & 0x20) << (t - 5)) | (buf[i] >> 7) | (*data << 1);
+ return t << 1;
+ }
+ return 0;
+}
+
+/*
+ * js_lt_reverse() reverses the order of bits in a byte.
+ */
+
+static unsigned char js_lt_reverse(unsigned char u)
+{
+ u = ((u & 0x0f) << 4) | ((u >> 4) & 0x0f);
+ u = ((u & 0x33) << 2) | ((u >> 2) & 0x33);
+ u = ((u & 0x55) << 1) | ((u >> 1) & 0x55);
+ return u;
+}
+
+/*
+ * js_lt_read() reads and analyzes Logitech joystick data.
+ */
+
+static int js_lt_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_lt_info *info = xinfo;
+ __u64 data;
+ int hat;
+
+ switch (info->mode) {
+
+ case JS_LT_MODE_TPD:
+
+ if (js_lt_read_packet(info->io, &data) != 20) return -1;
+
+ axes[0][0] = ((data >> 6) & 1) - ((data >> 4) & 1);
+ axes[0][1] = ((data >> 5) & 1) - ((data >> 7) & 1);
+
+ buttons[0][0] = js_lt_reverse((data & 0x0f) | ((data >> 4) & 0xf0));
+
+ return 0;
+
+ case JS_LT_MODE_WMED:
+
+ if (js_lt_read_packet(info->io, &data) != 42) return -1;
+ if ((hat = data & 0xf) > 8) return -1;
+
+ axes[0][0] = (data >> 26) & 0xff;
+ axes[0][1] = (data >> 18) & 0xff;
+ axes[0][2] = (data >> 10) & 0xff;
+ axes[0][3] = js_lt_hat_to_axis[hat].x;
+ axes[0][4] = js_lt_hat_to_axis[hat].y;
+
+ buttons[0][0] = js_lt_reverse((data >> 2) & 0xfc);
+
+ return 0;
+
+ case JS_LT_MODE_CM2:
+
+ if (js_lt_read_packet(info->io, &data) != 64) return -1;
+
+ axes[0][0] = (data >> 48) & 0xff;
+ axes[0][1] = (data >> 40) & 0xff;
+ axes[0][2] = (data >> 32) & 0xff;
+ axes[0][3] = (data >> 24) & 0xff;
+ axes[0][4] = (data >> 16) & 0xff;
+ axes[0][5] = (data >> 8) & 0xff;
+
+ buttons[0][0] = js_lt_reverse(data & 0xff);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * js_lt_open() is a callback from the file open routine.
+ */
+
+static int js_lt_open(struct js_dev *jd)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_lt_close() is a callback from the file release routine.
+ */
+
+static int js_lt_close(struct js_dev *jd)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_lt_trigger_sequence() sends a trigger & delay sequence
+ * to reset/initialize a Logitech joystick.
+ */
+
+static void __init js_lt_trigger_sequence(int io, int *seq)
+{
+ while (*seq) {
+ outb(0xff,io);
+ udelay(*seq++);
+ }
+}
+
+/*
+ * js_lt_init_corr() initializes the correction values for
+ * Logitech joysticks.
+ */
+
+static void __init js_lt_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr)
+{
+ int j;
+
+ for (j = 0; j < num_axes; j++) {
+ corr[0][j].type = JS_CORR_BROKEN;
+ corr[0][j].prec = 2;
+ corr[0][j].coef[0] = axes[0][j] - 8;
+ corr[0][j].coef[1] = axes[0][j] + 8;
+ corr[0][j].coef[2] = (1 << 29) / (127 - 32);
+ corr[0][j].coef[3] = (1 << 29) / (127 - 32);
+ }
+
+ switch (mode) {
+ case JS_LT_MODE_TPD: j = 0; break;
+ case JS_LT_MODE_WMED: j = 3; break;
+ case JS_LT_MODE_CM2: j = 6; break;
+ default: j = 0; break;
+ }
+
+ for (; j < num_axes; j++) {
+ corr[0][j].type = JS_CORR_BROKEN;
+ corr[0][j].prec = 0;
+ corr[0][j].coef[0] = 0;
+ corr[0][j].coef[1] = 0;
+ corr[0][j].coef[2] = (1 << 29);
+ corr[0][j].coef[3] = (1 << 29);
+ }
+
+}
+
+/*
+ * js_lt_probe() probes for Logitech type joysticks.
+ */
+
+static struct js_port __init *js_lt_probe(int io, struct js_port *port)
+{
+ struct js_lt_info info;
+ char *name;
+ int axes, buttons, i;
+ __u64 data;
+ unsigned char u;
+
+ if (check_region(io, 1)) return port;
+
+ if (((u = inb(io)) & 3) == 3) return port;
+ outb(0xff,io);
+ if (!((inb(io) ^ u) & ~u & 0xf)) return port;
+
+ if (!(i = js_lt_read_packet(io, &data))) {
+ udelay(JS_LT_MAX_DELAY);
+ js_lt_trigger_sequence(io, js_lt_seq_reset);
+ js_lt_trigger_sequence(io, js_lt_seq_init);
+ i = js_lt_read_packet(io, &data);
+ }
+
+ switch (i) {
+ case 0:
+ return port;
+ case 20:
+ info.mode = JS_LT_MODE_TPD;
+ axes = 2; buttons = 8; name = "Logitech ThunderPad Digital";
+ break;
+ case 42:
+ info.mode = JS_LT_MODE_WMED;
+ axes = 5; buttons = 6; name = "Logitech WingMan Extreme Digital";
+ break;
+ case 64:
+ info.mode = JS_LT_MODE_CM2;
+ axes = 6; buttons = 8; name = "Logitech CyberMan 2";
+ break;
+ case 72:
+ case 144:
+ return port;
+ default:
+ printk(KERN_WARNING "joy-logitech: unknown joystick device detected "
+ "(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@ucw.cz>\n",
+ io, i, (int)(data >> 32), (int)(data & 0xffffffff));
+ return port;
+ }
+
+ info.io = io;
+
+ request_region(io, 1, "joystick (logitech)");
+ port = js_register_port(port, &info, 1, sizeof(struct js_lt_info), js_lt_read);
+ printk(KERN_INFO "js%d: %s at %#x\n",
+ js_register_device(port, 0, axes, buttons, name, js_lt_open, js_lt_close), name, io);
+
+ udelay(JS_LT_MAX_DELAY);
+
+ js_lt_read(port->info, port->axes, port->buttons);
+ js_lt_init_corr(axes, info.mode, port->axes, port->corr);
+
+ return port;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_lt_init(void)
+#endif
+{
+ int *p;
+
+ for (p = js_lt_port_list; *p; p++) js_lt_port = js_lt_probe(*p, js_lt_port);
+ if (js_lt_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-logitech: no joysticks found\n");
+#endif
+
+ return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ struct js_lt_info *info;
+
+ while (js_lt_port != NULL) {
+ js_unregister_device(js_lt_port->devs[0]);
+ info = js_lt_port->info;
+ release_region(info->io, 1);
+ js_lt_port = js_unregister_port(js_lt_port);
+ }
+}
+#endif