patch-2.1.125 linux/drivers/char/joystick/joy-sidewinder.c

Next file: linux/drivers/char/joystick/joy-thrustmaster.c
Previous file: linux/drivers/char/joystick/joy-logitech.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-sidewinder.c linux/drivers/char/joystick/joy-sidewinder.c
@@ -0,0 +1,471 @@
+/*
+ *  joy-sidewinder.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Microsoft SideWinder 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_SW_MAX_START		250
+#define JS_SW_MIN_STROBE	25
+#define JS_SW_EXT_STROBE	45
+#define JS_SW_MIN_TIME		1500
+#define JS_SW_MAX_TIME		4000
+
+#define JS_SW_MAX_LENGTH	72
+
+#define JS_SW_MODE_3DP		1
+#define JS_SW_MODE_PP		2
+#define JS_SW_MODE_GP		3
+
+static int js_sw_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_sw_port __initdata = NULL;
+
+static struct {
+	int x;
+	int y;
+} js_sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct js_sw_info {
+	int io;
+	unsigned char mode;
+	unsigned char number;
+	unsigned char optimize;
+};
+
+/*
+ * js_sw_init_digital() switches a SideWinder into digital mode.
+ * It currently switches to 2-bit mode only (FIXME).
+ */
+
+static void __init js_sw_init_digital(int io)
+{
+	unsigned int t;
+	unsigned int timeout = (js_time_speed * JS_SW_MAX_TIME) >> 10;
+	int delays[] = {100, 850, 400, 0};
+	int i = 0;
+	unsigned long flags;
+
+	__save_flags(flags);
+	__cli();
+	do {
+		outb(inb(io),io);
+		t = js_get_time();
+		while ((inb(io) & 1) && (js_delta(js_get_time(),t) < timeout));
+		udelay(delays[i]);
+	} while (delays[i++]);
+	__restore_flags(flags);
+
+	return;
+}
+
+/*
+ * js_sw_read_packet() reads a SideWinder packet.
+ */
+
+static int js_sw_read_packet(int io, int l1, int l2, int strobe, __u64 *data)
+{
+	static unsigned char buf[JS_SW_MAX_LENGTH];
+	unsigned char u, v;
+	int i;
+	unsigned long flags;
+	unsigned int t, t1;
+
+	int length = l1 < l2 ? l2 : l1;
+	int start = (js_time_speed * JS_SW_MAX_START) >> 10;
+	strobe = (js_time_speed * strobe) >> 10;
+
+	i = 0;
+
+	__save_flags(flags);
+	__cli();
+	outb(inb(io),io);
+
+	v = inb(io);
+	t = js_get_time();
+
+	do {
+		u = v;
+		v = inb(io);
+		t1 = js_get_time();
+	} while (!((u ^ v) & u & 0x10) && js_delta(t1, t) < start);
+
+	t = t1;
+
+	do {
+		v = inb(io);
+		t1 = js_get_time();
+		if ((u ^ v) & v & 0x10) {
+			buf[i++] = v >> 5;
+			t = t1;
+		}
+		u = v;
+	} while (i < length && js_delta(t1,t) < strobe);
+
+	__restore_flags(flags);
+
+	*data = 0;
+	t = i;
+
+	if (t == l1) {
+		for (i = 0; i < t; i++)
+			*data |= (__u64) (buf[i] & 1) << i;
+		return t;
+	}
+	if (t == l2) {
+		for (i = 0; i < t; i++)
+			*data |= (__u64) buf[i] << (3 * i);
+		return t * 3;
+	}
+	return t;
+}
+
+/*
+ * js_sw_parity computes parity of __u64
+ */
+
+static int js_sw_parity(__u64 t)
+{
+	t ^= t >> 32;
+	t ^= t >> 16;
+	t ^= t >> 8;
+	t ^= t >> 4;
+	t ^= t >> 2;
+	t ^= t >> 1;
+	return t & 1;
+}
+
+/*
+ * js_sw_csum() computes checksum of nibbles in __u64
+ */
+
+static int js_sw_csum(__u64 t)
+{
+	char sum = 0;
+	while (t) {
+		sum += t & 0xf;
+		t >>= 4;
+	}
+	return sum & 0xf;
+}
+
+/*
+ * js_sw_read() reads and analyzes SideWinder joystick data.
+ */
+
+static int js_sw_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_sw_info *info = xinfo;
+	__u64 data;
+	int hat, i;
+
+	switch (info->mode) {
+
+		case JS_SW_MODE_3DP:
+
+			if (info->optimize) {
+				i = js_sw_read_packet(info->io, -1, 22, JS_SW_EXT_STROBE, &data);
+			} else {
+				i = js_sw_read_packet(info->io, 64, 66, JS_SW_EXT_STROBE, &data);
+				if (i == 192) info->optimize = 1;
+			}
+
+			if (i < 60) {
+				js_sw_init_digital(info->io);
+				info->optimize = 0;
+				return -1;
+			}
+
+			if (((data & 0x8080808080808080ULL) ^ 0x80) || js_sw_csum(data) ||
+				(hat = ((data >> 3) & 0x08) | ((data >> 60) & 0x07)) > 8) {
+				info->optimize = 0;
+				return -1;
+			}
+
+			axes[0][0] = ((data <<  4) & 0x380) | ((data >> 16) & 0x07f);
+			axes[0][1] = ((data <<  7) & 0x380) | ((data >> 24) & 0x07f);
+			axes[0][2] = ((data >> 28) & 0x180) | ((data >> 40) & 0x07f);
+			axes[0][3] = ((data >> 25) & 0x380) | ((data >> 48) & 0x07f);
+			axes[0][4] = js_sw_hat_to_axis[hat].x;
+			axes[0][5] = js_sw_hat_to_axis[hat].y;
+			buttons[0][0] = ((~data >> 31) & 0x80) | ((~data >> 8) & 0x7f);
+
+			return 0;
+
+		case JS_SW_MODE_PP:
+
+			if (js_sw_read_packet(info->io, 48, 16, JS_SW_EXT_STROBE, &data) != 48) return -1;
+			if (!js_sw_parity(data) || (hat = (data >> 42) & 0xf) > 8) return -1;
+
+			axes[0][0] = (data >>  9) & 0x3ff;
+			axes[0][1] = (data >> 19) & 0x3ff;
+			axes[0][2] = (data >> 29) & 0x07f;
+			axes[0][3] = (data >> 36) & 0x03f;
+			axes[0][4] = js_sw_hat_to_axis[hat].x;
+			axes[0][5] = js_sw_hat_to_axis[hat].y;
+			buttons[0][0] = ~data & 0x1ff;
+
+			return 0;
+
+		case JS_SW_MODE_GP:
+
+			if (js_sw_read_packet(info->io, 15 * info->number, 5 * info->number,
+				JS_SW_EXT_STROBE, &data) != 15 * info->number) return -1;
+			if (js_sw_parity(data)) return -1;
+
+			for (i = 0; i < info->number; i++) {
+				axes[i][0] = ((data >> 3) & 1) - ((data >> 2) & 1);
+				axes[i][1] = ( data       & 1) - ((data >> 1) & 1);
+				buttons[i][0] = (~data >> 4) & 0x3ff;
+				data >>= 15;
+			}
+
+			return 0;
+
+		default:
+			return -1;
+	}
+}
+
+/*
+ * js_sw_open() is a callback from the file open routine.
+ */
+
+static int js_sw_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_sw_close() is a callback from the file release routine.
+ */
+
+static int js_sw_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_sw_init_corr() initializes the correction values for
+ * SideWinders.
+ */
+
+static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js_corr **corr)
+{
+	int i, j;
+
+	for (i = 0; i < number; i++) {
+
+		for (j = 0; j < num_axes; j++) {
+			corr[i][j].type = JS_CORR_BROKEN;
+			corr[i][j].prec = 8;
+			corr[i][j].coef[0] = 511 - 32;
+			corr[i][j].coef[1] = 512 + 32;
+			corr[i][j].coef[2] = (1 << 29) / (511 - 32);
+			corr[i][j].coef[3] = (1 << 29) / (511 - 32);
+		}
+
+		switch (mode) {
+
+			case JS_SW_MODE_3DP:
+
+				corr[i][2].type = JS_CORR_BROKEN;
+				corr[i][2].prec = 4;
+				corr[i][2].coef[0] = 255 - 16;
+				corr[i][2].coef[1] = 256 + 16;
+				corr[i][2].coef[2] = (1 << 29) / (255 - 16);
+				corr[i][2].coef[3] = (1 << 29) / (255 - 16);
+
+				j = 4;
+
+			break;
+
+			case JS_SW_MODE_PP:
+
+				corr[i][2].type = JS_CORR_BROKEN;
+				corr[i][2].prec = 1;
+				corr[i][2].coef[0] = 63 - 4;
+				corr[i][2].coef[1] = 64 + 4;
+				corr[i][2].coef[2] = (1 << 29) / (63 - 4);
+				corr[i][2].coef[3] = (1 << 29) / (63 - 4);
+
+				corr[i][3].type = JS_CORR_BROKEN;
+				corr[i][3].prec = 0;
+				corr[i][3].coef[0] = 31 - 2;
+				corr[i][3].coef[1] = 32 + 2;
+				corr[i][3].coef[2] = (1 << 29) / (31 - 2);
+				corr[i][3].coef[3] = (1 << 29) / (31 - 2);
+
+				j = 4;
+
+			break;
+
+			default:
+
+				j = 0;
+
+		}
+
+		for (; j < num_axes; j++) {
+			corr[i][j].type = JS_CORR_BROKEN;
+			corr[i][j].prec = 0;
+			corr[i][j].coef[0] = 0;
+			corr[i][j].coef[1] = 0;
+			corr[i][j].coef[2] = (1 << 29);
+			corr[i][j].coef[3] = (1 << 29);
+		}
+	}
+}
+
+/*
+ * js_sw_probe() probes for SideWinder type joysticks.
+ */
+
+static struct js_port __init *js_sw_probe(int io, struct js_port *port)
+{
+	struct js_sw_info info;
+	char *name;
+	int i, j, axes, buttons;
+	__u64 data;
+	unsigned char u;
+
+
+	if (check_region(io, 1)) return port;
+	if (((u = inb(io)) & 3) == 3) return port;
+	outb(u,io);
+	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
+
+	i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data);
+
+	if (!i) {
+		udelay(JS_SW_MIN_TIME);
+		js_sw_init_digital(io);
+		udelay(JS_SW_MAX_TIME);
+		i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data);
+	}
+
+	switch (i) {
+		case 0:
+			return port;
+		case 5:
+		case 10:
+		case 15:
+		case 20:
+		case 30:
+		case 45:
+		case 60:
+			info.mode = JS_SW_MODE_GP;
+			outb(inb(io),io);						/* Kick into 3-bit mode */
+			udelay(JS_SW_MAX_TIME);
+			i = js_sw_read_packet(io, 60, -1, JS_SW_EXT_STROBE, &data);	/* Get total length */
+			udelay(JS_SW_MIN_TIME);
+			j = js_sw_read_packet(io, 15, -1, JS_SW_MIN_STROBE, &data);	/* Get subpacket length */
+			if (!i || !j) {
+				printk(KERN_WARNING "joy-sidewinder: SideWinder GamePad detected (%d,%d),"
+							" but not idenfitied.\n", i, j);
+				return port;
+			}
+			info.number = i / j;
+			axes = 2; buttons = 10; name = "SideWinder GamePad";
+			break;
+		case 16:
+		case 48:
+			info.mode = JS_SW_MODE_PP; info.number = 1;
+			axes = 6; buttons = 9; name = "SideWinder Precision Pro";
+			break;
+		case 64:
+		case 66:
+			info.mode = JS_SW_MODE_3DP; info.number = 1; info.optimize = 0;
+			axes = 6; buttons = 8; name = "SideWinder 3D Pro";
+			break;
+		case 72:
+			return port;
+		default:
+			printk(KERN_WARNING "joy-sidewinder: 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 (sidewinder)");
+	port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read);
+	for (i = 0; i < info.number; i++)
+		printk(KERN_INFO "js%d: %s at %#x\n",
+			js_register_device(port, i, axes, buttons, name, js_sw_open, js_sw_close), name, io);
+	js_sw_init_corr(axes, info.mode, info.number, port->corr);
+
+	return port;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_sw_init(void)
+#endif
+{
+	int *p;
+
+	for (p = js_sw_port_list; *p; p++) js_sw_port = js_sw_probe(*p, js_sw_port);
+	if (js_sw_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-sidewinder: no joysticks found\n");
+#endif
+
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int i;
+	struct js_sw_info *info;
+
+	while (js_sw_port) {
+		for (i = 0; i < js_sw_port->ndevs; i++)
+			if (js_sw_port->devs[i])
+				js_unregister_device(js_sw_port->devs[i]);
+		info = js_sw_port->info;
+		release_region(info->io, 1);
+		js_sw_port = js_unregister_port(js_sw_port);
+	}
+
+}
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov