summaryrefslogtreecommitdiffstats
path: root/drivers/char/joystick
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/joystick')
-rw-r--r--drivers/char/joystick/Config.in19
-rw-r--r--drivers/char/joystick/Makefile112
-rw-r--r--drivers/char/joystick/joy-amiga.c157
-rw-r--r--drivers/char/joystick/joy-analog.c208
-rw-r--r--drivers/char/joystick/joy-analog.h297
-rw-r--r--drivers/char/joystick/joy-assasin.c423
-rw-r--r--drivers/char/joystick/joy-console.c596
-rw-r--r--drivers/char/joystick/joy-db9.c406
-rw-r--r--drivers/char/joystick/joy-gravis.c401
-rw-r--r--drivers/char/joystick/joy-lightning.c366
-rw-r--r--drivers/char/joystick/joy-logitech.c371
-rw-r--r--drivers/char/joystick/joy-sidewinder.c476
-rw-r--r--drivers/char/joystick/joy-thrustmaster.c331
-rw-r--r--drivers/char/joystick/joy-turbografx.c281
-rw-r--r--drivers/char/joystick/joystick.c1231
15 files changed, 5675 insertions, 0 deletions
diff --git a/drivers/char/joystick/Config.in b/drivers/char/joystick/Config.in
new file mode 100644
index 000000000..db5585cd1
--- /dev/null
+++ b/drivers/char/joystick/Config.in
@@ -0,0 +1,19 @@
+#
+# Joystick lowlevel driver configuration
+#
+
+dep_tristate ' Classic PC analog joysticks and gamepads' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK
+dep_tristate ' FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK
+dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK
+dep_tristate ' Logitech Digital joysticks and gamepads' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK
+dep_tristate ' Microsoft SideWinder, Genius Digital joysticks and gamepads' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK
+dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK
+dep_tristate ' PDPI Lightning 4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK
+if [ "$CONFIG_PARPORT" != "n" ]; then
+ dep_tristate ' NES, SNES, PSX, Multisystem joysticks and gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK
+ dep_tristate ' Sega, Multisystem joysticks and gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK
+ dep_tristate ' TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK
+fi
+if [ "$CONFIG_AMIGA" = "y" ]; then
+ dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK
+fi
diff --git a/drivers/char/joystick/Makefile b/drivers/char/joystick/Makefile
new file mode 100644
index 000000000..3339fc83f
--- /dev/null
+++ b/drivers/char/joystick/Makefile
@@ -0,0 +1,112 @@
+#
+# Makefile for the joystick drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+O_TARGET := js.o
+O_OBJS :=
+M_OBJS :=
+
+ifeq ($(CONFIG_JOYSTICK),y)
+O_OBJS += joystick.o
+else
+ ifeq ($(CONFIG_JOYSTICK),m)
+ M_OBJS += joystick.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_AMIGA),y)
+O_OBJS += joy-amiga.o
+else
+ ifeq ($(CONFIG_JOY_AMIGA),m)
+ M_OBJS += joy-amiga.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_ANALOG),y)
+O_OBJS += joy-analog.o
+else
+ ifeq ($(CONFIG_JOY_ANALOG),m)
+ M_OBJS += joy-analog.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_ASSASIN),y)
+O_OBJS += joy-assasin.o
+else
+ ifeq ($(CONFIG_JOY_ASSASIN),m)
+ M_OBJS += joy-assasin.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_CONSOLE),y)
+O_OBJS += joy-console.o
+else
+ ifeq ($(CONFIG_JOY_CONSOLE),m)
+ M_OBJS += joy-console.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_DB9),y)
+O_OBJS += joy-db9.o
+else
+ ifeq ($(CONFIG_JOY_DB9),m)
+ M_OBJS += joy-db9.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_GRAVIS),y)
+O_OBJS += joy-gravis.o
+else
+ ifeq ($(CONFIG_JOY_GRAVIS),m)
+ M_OBJS += joy-gravis.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_LIGHTNING),y)
+O_OBJS += joy-lightning.o
+else
+ ifeq ($(CONFIG_JOY_LIGHTNING),m)
+ M_OBJS += joy-lightning.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_LOGITECH),y)
+O_OBJS += joy-logitech.o
+else
+ ifeq ($(CONFIG_JOY_LOGITECH),m)
+ M_OBJS += joy-logitech.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_SIDEWINDER),y)
+O_OBJS += joy-sidewinder.o
+else
+ ifeq ($(CONFIG_JOY_SIDEWINDER),m)
+ M_OBJS += joy-sidewinder.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_THRUSTMASTER),y)
+O_OBJS += joy-thrustmaster.o
+else
+ ifeq ($(CONFIG_JOY_THRUSTMASTER),m)
+ M_OBJS += joy-thrustmaster.o
+ endif
+endif
+
+ifeq ($(CONFIG_JOY_TURBOGRAFX),y)
+O_OBJS += joy-turbografx.o
+else
+ ifeq ($(CONFIG_JOY_TURBOGRAFX),m)
+ M_OBJS += joy-turbografx.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/char/joystick/joy-amiga.c b/drivers/char/joystick/joy-amiga.c
new file mode 100644
index 000000000..520f262a4
--- /dev/null
+++ b/drivers/char/joystick/joy-amiga.c
@@ -0,0 +1,157 @@
+/*
+ * joy-amiga.c Version 1.2
+ *
+ * Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * microswitch based joystick connected to Amiga joystick port.
+ */
+
+/*
+ * 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/system.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/amigahw.h>
+
+static struct js_port* js_am_port __initdata = NULL;
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_am, "1-2i");
+
+static int js_am[]={0,0};
+
+/*
+ * js_am_read() reads and Amiga joystick data.
+ */
+
+static int js_am_read(void *info, int **axes, int **buttons)
+{
+ int data = 0;
+
+ switch (*(int*)info) {
+ case 0:
+ data = ~custom.joy0dat;
+ buttons[0][0] = (~ciaa.pra >> 6) & 1;
+ break;
+
+ case 1:
+ data = ~custom.joy1dat;
+ buttons[0][0] = (~ciaa.pra >> 7) & 1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1);
+ data = ~(data ^ (data << 1));
+ axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1);
+
+ return 0;
+}
+
+/*
+ * js_am_open() is a callback from the file open routine.
+ */
+
+static int js_am_open(struct js_dev *jd)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_am_close() is a callback from the file release routine.
+ */
+
+static int js_am_close(struct js_dev *jd)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_am_init_corr() initializes correction values of
+ * Amiga joysticks.
+ */
+
+static void __init js_am_init_corr(struct js_corr **corr)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ corr[0][i].type = JS_CORR_BROKEN;
+ corr[0][i].prec = 0;
+ corr[0][i].coef[0] = 0;
+ corr[0][i].coef[1] = 0;
+ corr[0][i].coef[2] = (1 << 29);
+ corr[0][i].coef[3] = (1 << 29);
+ }
+}
+
+#ifndef MODULE
+void __init js_am_setup(char *str, int *ints)
+{
+ int i;
+ for (i = 0; i <= ints[0] && i < 2; i++) js_am[i] = ints[i+1];
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_am_init(void)
+#endif
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (js_am[i]) {
+ js_am_port = js_register_port(js_am_port, &i, 1, sizeof(int), js_am_read);
+ printk(KERN_INFO "js%d: Amiga joystick at joy%ddat\n",
+ js_register_device(js_am_port, 0, 2, 1, "Amiga joystick", js_am_open, js_am_close), i);
+ js_am_init_corr(js_am_port->corr);
+ }
+ if (js_am_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-amiga: no joysticks specified\n");
+#endif
+
+ return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ while (js_am_port != NULL) {
+ if (js_am_port->devs[0] != NULL)
+ js_unregister_device(js_am_port->devs[0]);
+ js_am_port = js_unregister_port(js_am_port);
+ }
+}
+#endif
diff --git a/drivers/char/joystick/joy-analog.c b/drivers/char/joystick/joy-analog.c
new file mode 100644
index 000000000..fdf04b212
--- /dev/null
+++ b/drivers/char/joystick/joy-analog.c
@@ -0,0 +1,208 @@
+/*
+ * joy-analog.c Version 1.2
+ *
+ * Copyright (c) 1996-1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * up to two analog (or CHF/FCS) joysticks on a single joystick port.
+ */
+
+/*
+ * 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 <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_AN_MAX_TIME 3000
+
+static int js_an_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_an_port __initdata = NULL;
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.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};
+
+#include "joy-analog.h"
+
+/*
+ * js_an_read() reads analog joystick data.
+ */
+
+static int js_an_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_an_info *info = xinfo;
+ unsigned char buf[4];
+ int time[4];
+ unsigned char u, v, a;
+ unsigned int t, t1;
+ 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;
+
+ 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);
+
+ outb(0xff,io);
+ t = js_get_time_a();
+ do {
+ v = inb(io) & a;
+ t1 = js_get_time_a();
+ if (u ^ v) {
+ time[i] = js_delta_a(t1,t);
+ buf[i] = u ^ v;
+ u = v;
+ i++;
+ }
+ } while (v && js_delta_a(t1,t) < timeout);
+
+ for (--i; i >= 0; i--)
+ for (j = 0; j < 4; j++)
+ if (buf[i] & (1 << j)) info->axes[j] = (time[i] << 10) / js_time_speed_a;
+
+ js_an_decode(info, axes, buttons);
+
+ return 0;
+}
+
+/*
+ * js_an_open() is a callback from the file open routine.
+ */
+
+static int js_an_open(struct js_dev *jd)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_an_close() is a callback from the file release routine.
+ */
+
+static int js_an_close(struct js_dev *jd)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * 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;
+ int i, numdev;
+ unsigned char u;
+
+ if (io < 0) return port;
+
+ 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);
+ u = (inb(io) ^ u) & u;
+
+ if (!u) return port;
+ if (u & 0xf0) return port;
+
+ if ((numdev = js_an_probe_devs(&info, u, mask0, mask1, port)) <= 0)
+ return port;
+
+ info.io = io;
+ request_region(info.io, 1, "joystick (analog)");
+ port = js_register_port(port, &info, numdev, sizeof(struct js_an_info), js_an_read);
+
+ 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);
+
+ js_an_read(port->info, port->axes, port->buttons);
+ js_an_init_corr(port->info, port->axes, port->corr, 8);
+
+ return port;
+}
+
+#ifndef MODULE
+void __init js_an_setup(char *str, int *ints)
+{
+ int i;
+ for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1];
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_an_init(void)
+#endif
+{
+ int i;
+
+ if (js_an[0] >= 0) {
+ for (i = 0; (js_an[i*3] >= 0) && i < 8; i++)
+ js_an_port = js_an_probe(js_an[i*3], js_an[i*3+1], js_an[i*3+2], js_an_port);
+ } else {
+ for (i = 0; js_an_port_list[i]; i++)
+ js_an_port = js_an_probe(js_an_port_list[i], 0, 0, js_an_port);
+ }
+ if (js_an_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-analog: no joysticks found\n");
+#endif
+
+ return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ int i;
+ struct js_an_info *info;
+
+ while (js_an_port != NULL) {
+ for (i = 0; i < js_an_port->ndevs; i++)
+ if (js_an_port->devs[i] != NULL)
+ js_unregister_device(js_an_port->devs[i]);
+ info = js_an_port->info;
+ release_region(info->io, 1);
+ js_an_port = js_unregister_port(js_an_port);
+ }
+
+}
+#endif
diff --git a/drivers/char/joystick/joy-analog.h b/drivers/char/joystick/joy-analog.h
new file mode 100644
index 000000000..06c0079a4
--- /dev/null
+++ b/drivers/char/joystick/joy-analog.h
@@ -0,0 +1,297 @@
+/*
+ * joy-analog.h Version 1.2
+ *
+ * Copyright (c) 1996-1998 Vojtech Pavlik
+ */
+
+/*
+ * This file is designed to be included in any joystick driver
+ * that communicates with standard analog joysticks. This currently
+ * is: joy-analog.c, joy-assasin.c, and joy-lightning.c
+ */
+
+/*
+ * 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
+ */
+
+#define JS_AN_AXES_STD 0x0f
+#define JS_AN_BUTTONS_STD 0xf0
+
+#define JS_AN_BUTTONS_CHF 0x01
+#define JS_AN_HAT1_CHF 0x02
+#define JS_AN_HAT2_CHF 0x04
+#define JS_AN_ANY_CHF 0x07
+#define JS_AN_HAT_FCS 0x08
+#define JS_AN_HATS_ALL 0x0e
+#define JS_AN_BUTTON_PXY_X 0x10
+#define JS_AN_BUTTON_PXY_Y 0x20
+#define JS_AN_BUTTON_PXY_U 0x40
+#define JS_AN_BUTTON_PXY_V 0x80
+#define JS_AN_BUTTONS_PXY_XY 0x30
+#define JS_AN_BUTTONS_PXY_UV 0xc0
+#define JS_AN_BUTTONS_PXY 0xf0
+
+static struct {
+ int x;
+ int y;
+} js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}};
+
+struct js_an_info {
+ int io;
+ unsigned char mask[2];
+ unsigned int extensions;
+ int axes[4];
+ int initial[4];
+ unsigned char buttons;
+};
+
+/*
+ * js_an_decode() decodes analog joystick data.
+ */
+
+static void js_an_decode(struct js_an_info *info, int **axes, int **buttons)
+{
+ int i, j, k;
+ int hat1, hat2, hat3;
+
+ hat1 = hat2 = hat3 = 0;
+ if (info->mask[0] & JS_AN_BUTTONS_STD) buttons[0][0] = 0;
+ if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0;
+
+ if (info->extensions & JS_AN_ANY_CHF) {
+ switch (info->buttons) {
+ case 0x1: buttons[0][0] = 0x01; break;
+ case 0x2: buttons[0][0] = 0x02; break;
+ case 0x4: buttons[0][0] = 0x04; break;
+ case 0x8: buttons[0][0] = 0x08; break;
+ case 0x5: buttons[0][0] = 0x10; break;
+ case 0x9: buttons[0][0] = 0x20; break;
+ case 0xf: hat1 = 1; break;
+ case 0xb: hat1 = 2; break;
+ case 0x7: hat1 = 3; break;
+ case 0x3: hat1 = 4; break;
+ case 0xe: hat2 = 1; break;
+ case 0xa: hat2 = 2; break;
+ case 0x6: hat2 = 3; break;
+ case 0xc: hat2 = 4; break;
+ }
+ k = info->extensions & JS_AN_BUTTONS_CHF ? 6 : 4;
+ } else {
+ for (i = 1; i >= 0; i--)
+ for (j = k = 0; j < 4; j++)
+ if (info->mask[i] & (0x10 << j))
+ buttons[i][0] |= ((info->buttons >> j) & 1) << k++;
+ }
+
+ if (info->extensions & JS_AN_BUTTON_PXY_X)
+ buttons[0][0] |= (info->axes[2] < (info->initial[2] >> 1)) << k++;
+ if (info->extensions & JS_AN_BUTTON_PXY_Y)
+ buttons[0][0] |= (info->axes[3] < (info->initial[3] >> 1)) << k++;
+ if (info->extensions & JS_AN_BUTTON_PXY_U)
+ buttons[0][0] |= (info->axes[2] > (info->initial[2] + (info->initial[2] >> 1))) << k++;
+ if (info->extensions & JS_AN_BUTTON_PXY_V)
+ buttons[0][0] |= (info->axes[3] > (info->initial[3] + (info->initial[3] >> 1))) << k++;
+
+ if (info->extensions & JS_AN_HAT_FCS)
+ for (j = 0; j < 4; j++)
+ if (info->axes[3] < ((info->initial[3] * ((j << 1) + 1)) >> 3)) {
+ hat3 = j + 1;
+ break;
+ }
+
+ for (i = 1; i >= 0; i--)
+ for (j = k = 0; j < 4; j++)
+ if (info->mask[i] & (1 << j))
+ axes[i][k++] = info->axes[j];
+
+ if (info->extensions & JS_AN_HAT1_CHF) {
+ axes[0][k++] = js_an_hat_to_axis[hat1].x;
+ axes[0][k++] = js_an_hat_to_axis[hat1].y;
+ }
+ if (info->extensions & JS_AN_HAT2_CHF) {
+ axes[0][k++] = js_an_hat_to_axis[hat2].x;
+ axes[0][k++] = js_an_hat_to_axis[hat2].y;
+ }
+ if (info->extensions & JS_AN_HAT_FCS) {
+ axes[0][k++] = js_an_hat_to_axis[hat3].x;
+ axes[0][k++] = js_an_hat_to_axis[hat3].y;
+ }
+}
+
+/*
+ * js_an_count_bits() counts set bits in a byte.
+ */
+
+static inline int js_an_count_bits(unsigned long c)
+{
+ int i = 0;
+ while (c) {
+ i += c & 1;
+ c >>= 1;
+ }
+ return i;
+}
+
+/*
+ * js_an_init_corr() initializes the correction values for
+ * analog joysticks.
+ */
+
+static void __init js_an_init_corr(struct js_an_info *info, int **axes, struct js_corr **corr, int prec)
+{
+ int i, j, t;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < js_an_count_bits(info->mask[i] & 0xf); j++) {
+
+ if ((j == 2 && (info->mask[i] & 0xb) == 0xb) ||
+ (j == 3 && (info->mask[i] & 0xf) == 0xf)) {
+ t = (axes[i][0] + axes[i][1]) >> 1;
+ } else {
+ t = axes[i][j];
+ }
+
+ corr[i][j].type = JS_CORR_BROKEN;
+ corr[i][j].prec = prec;
+ corr[i][j].coef[0] = t - (t >> 3);
+ corr[i][j].coef[1] = t + (t >> 3);
+ corr[i][j].coef[2] = (1 << 29) / (t - (t >> 2) + 1);
+ corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1);
+ }
+
+ i = js_an_count_bits(info->mask[0] & 0xf);
+
+ for (j = i; j < i + (js_an_count_bits(info->extensions & JS_AN_HATS_ALL) << 1); 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);
+ }
+
+ for (i = 0; i < 4; i++)
+ info->initial[i] = info->axes[i];
+}
+
+
+/*
+ * js_an_probe_devs() probes for analog joysticks.
+ */
+
+static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0, int mask1, struct js_port *port)
+{
+ info->mask[0] = info->mask[1] = info->extensions = 0;
+
+ if (mask0 || mask1) {
+ info->mask[0] = mask0 & (exist | 0xf0);
+ info->mask[1] = mask1 & (exist | 0xf0) & ~info->mask[0];
+ info->extensions = (mask0 >> 8) & ((exist & JS_AN_HAT_FCS) | ((exist << 2) & JS_AN_BUTTONS_PXY_XY) |
+ ((exist << 4) & JS_AN_BUTTONS_PXY_UV) | JS_AN_ANY_CHF);
+ if (info->extensions & JS_AN_BUTTONS_PXY) {
+ info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2);
+ info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4);
+ info->mask[1] = 0;
+ }
+ if (info->extensions & JS_AN_HAT_FCS) {
+ info->mask[0] &= ~JS_AN_HAT_FCS;
+ info->mask[1] = 0;
+ info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_U);
+ }
+ if (info->extensions & JS_AN_ANY_CHF) {
+ info->mask[0] |= 0xf0;
+ info->mask[1] = 0;
+ }
+ if (!(info->mask[0] | info->mask[1])) return -1;
+ } else {
+ switch (exist) {
+ case 0x0:
+ return -1;
+ case 0x3:
+ info->mask[0] = 0xf3; /* joystick 0, assuming 4-button */
+ break;
+ case 0xb:
+ info->mask[0] = 0xfb; /* 3-axis, 4-button joystick */
+ break;
+ case 0xc:
+ info->mask[0] = 0xcc; /* joystick 1 */
+ break;
+ case 0xf:
+ info->mask[0] = 0x33; /* joysticks 0 and 1 */
+ info->mask[1] = 0xcc;
+ break;
+ default:
+ printk(KERN_WARNING "joy-analog: Unknown joystick device detected "
+ "(data=%#x), contact <vojtech@ucw.cz>\n", exist);
+ return -1;
+ }
+ }
+
+ return !!info->mask[0] + !!info->mask[1];
+}
+
+/*
+ * js_an_axes() returns the number of axes for an analog joystick.
+ */
+
+static inline int js_an_axes(int i, struct js_an_info *info)
+{
+ return js_an_count_bits(info->mask[i] & 0x0f) + js_an_count_bits(info->extensions & JS_AN_HATS_ALL) * 2;
+}
+
+/*
+ * js_an_buttons() returns the number of buttons for an analog joystick.
+ */
+
+static inline int js_an_buttons(int i, struct js_an_info *info)
+{
+ return js_an_count_bits(info->mask[i] & 0xf0) +
+ (info->extensions & JS_AN_BUTTONS_CHF) * 2 +
+ js_an_count_bits(info->extensions & JS_AN_BUTTONS_PXY);
+}
+
+/*
+ * js_an_name() constructs a name for an analog joystick.
+ */
+
+static char js_an_name_buf[128] __initdata = "";
+
+static char __init *js_an_name(int i, struct js_an_info *info)
+{
+
+ sprintf(js_an_name_buf, "Analog %d-axis %d-button",
+ js_an_count_bits(info->mask[i] & 0x0f),
+ js_an_buttons(i, info));
+
+ if (info->extensions & JS_AN_HATS_ALL)
+ sprintf(js_an_name_buf, "%s %d-hat",
+ js_an_name_buf,
+ js_an_count_bits(info->extensions & JS_AN_HATS_ALL));
+
+ strcat(js_an_name_buf, " joystick");
+
+ if (info->extensions)
+ sprintf(js_an_name_buf, "%s with%s%s%s extensions",
+ js_an_name_buf,
+ info->extensions & JS_AN_ANY_CHF ? " CHF" : "",
+ info->extensions & JS_AN_HAT_FCS ? " FCS" : "",
+ info->extensions & JS_AN_BUTTONS_PXY ? " XY-button" : "");
+
+ return js_an_name_buf;
+}
diff --git a/drivers/char/joystick/joy-assasin.c b/drivers/char/joystick/joy-assasin.c
new file mode 100644
index 000000000..64d16d7b0
--- /dev/null
+++ b/drivers/char/joystick/joy-assasin.c
@@ -0,0 +1,423 @@
+/*
+ * joy-assasin.c Version 1.2
+ *
+ * Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * joysticks using FP-Gaming's Assasin 3D protocol.
+ */
+
+/*
+ * 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_AS_MAX_START 250
+#define JS_AS_MAX_STROBE 50
+#define JS_AS_MAX_TIME 2400
+#define JS_AS_MAX_LENGTH 40
+
+#define JS_AS_MODE_A3D 1 /* Assasin 3D */
+#define JS_AS_MODE_PAN 2 /* Panther */
+#define JS_AS_MODE_OEM 3 /* Panther OEM version */
+#define JS_AS_MODE_PXL 4 /* Panther XL */
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_as, "2-24i");
+
+static int js_as[]={-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 js_as_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_as_port __initdata = NULL;
+
+#include "joy-analog.h"
+
+struct js_as_info {
+ int io;
+ char mode;
+ char rudder;
+ struct js_an_info an;
+};
+
+/*
+ * js_as_read_packet() reads an Assasin 3D packet.
+ */
+
+static int js_as_read_packet(int io, int length, char *data)
+{
+ unsigned char u, v;
+ int i;
+ unsigned int t, t1;
+ unsigned long flags;
+
+ int start = (js_time_speed * JS_AS_MAX_START) >> 10;
+ int strobe = (js_time_speed * JS_AS_MAX_STROBE) >> 10;
+
+ i = 0;
+
+ __save_flags(flags);
+ __cli();
+ outb(0xff,io);
+
+ u = inb(io);
+ t = js_get_time();
+
+ do {
+ v = inb(io);
+ t1 = js_get_time();
+ } while (u == v && js_delta(t1, t) < start);
+
+ t = t1;
+
+ do {
+ v = inb(io);
+ t1 = js_get_time();
+ if ((u ^ v) & u & 0x10) {
+ data[i++] = v >> 5;
+ t = t1;
+ }
+ u = v;
+ } while (i < length && js_delta(t1,t) < strobe);
+
+ __restore_flags(flags);
+
+ return i;
+}
+
+/*
+ * js_as_csum() computes checksum of triplet packet
+ */
+
+static int js_as_csum(char *data, int count)
+{
+ int i, csum = 0;
+ for (i = 0; i < count - 2; i++) csum += data[i];
+ return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]);
+}
+
+/*
+ * js_as_read() reads and analyzes A3D joystick data.
+ */
+
+static int js_as_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_as_info *info = xinfo;
+ char data[JS_AS_MAX_LENGTH];
+
+ switch (info->mode) {
+
+ case JS_AS_MODE_A3D:
+ case JS_AS_MODE_OEM:
+ case JS_AS_MODE_PAN:
+
+ if (js_as_read_packet(info->io, 29, data) != 29) return -1;
+ if (data[0] != info->mode) return -1;
+ if (js_as_csum(data, 29)) return -1;
+
+ axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7);
+ axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7);
+
+ buttons[0][0] = (data[2] << 2) | (data[3] >> 1);
+
+ info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128;
+ info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128;
+ info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128;
+ info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128;
+
+ info->an.buttons = ((data[3] << 3) | data[4]) & 0xf;
+
+ js_an_decode(&info->an, axes + 1, buttons + 1);
+
+ return 0;
+
+ case JS_AS_MODE_PXL:
+
+ if (js_as_read_packet(info->io, 33, data) != 33) return -1;
+ if (data[0] != info->mode) return -1;
+ if (js_as_csum(data, 33)) return -1;
+
+ axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128;
+ axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128;
+ info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128;
+ axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128;
+
+ axes[0][3] = ( data[5] & 1) - ((data[5] >> 2) & 1);
+ axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1);
+ axes[0][5] = ((data[4] >> 1) & 1) - ( data[3] & 1);
+ axes[0][6] = ((data[4] >> 2) & 1) - ( data[4] & 1);
+
+ axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7);
+ axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7);
+
+ buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8];
+
+ if (info->rudder) axes[1][0] = info->an.axes[0];
+
+ return 0;
+
+ default:
+ printk("Error.\n");
+ return -1;
+ }
+}
+
+/*
+ * js_as_open() is a callback from the file open routine.
+ */
+
+static int js_as_open(struct js_dev *jd)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_as_close() is a callback from the file release routine.
+ */
+
+static int js_as_close(struct js_dev *jd)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_as_pxl_init_corr() initializes the correction values for
+ * the Panther XL.
+ */
+
+static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ corr[0][i].type = JS_CORR_BROKEN;
+ corr[0][i].prec = 0;
+ corr[0][i].coef[0] = axes[0][i] - 4;
+ corr[0][i].coef[1] = axes[0][i] + 4;
+ corr[0][i].coef[2] = (1 << 29) / (127 - 32);
+ corr[0][i].coef[3] = (1 << 29) / (127 - 32);
+ }
+
+ corr[0][2].type = JS_CORR_BROKEN;
+ corr[0][2].prec = 0;
+ corr[0][2].coef[0] = 127 - 4;
+ corr[0][2].coef[1] = 128 + 4;
+ corr[0][2].coef[2] = (1 << 29) / (127 - 6);
+ corr[0][2].coef[3] = (1 << 29) / (127 - 6);
+
+ for (i = 3; i < 7; i++) {
+ corr[0][i].type = JS_CORR_BROKEN;
+ corr[0][i].prec = 0;
+ corr[0][i].coef[0] = 0;
+ corr[0][i].coef[1] = 0;
+ corr[0][i].coef[2] = (1 << 29);
+ corr[0][i].coef[3] = (1 << 29);
+ }
+
+ for (i = 7; i < 9; i++) {
+ corr[0][i].type = JS_CORR_BROKEN;
+ corr[0][i].prec = -1;
+ corr[0][i].coef[0] = 0;
+ corr[0][i].coef[1] = 0;
+ corr[0][i].coef[2] = (104 << 14);
+ corr[0][i].coef[3] = (104 << 14);
+ }
+}
+
+/*
+ * js_as_as_init_corr() initializes the correction values for
+ * the Panther and Assasin.
+ */
+
+static void __init js_as_as_init_corr(struct js_corr **corr)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ corr[0][i].type = JS_CORR_BROKEN;
+ corr[0][i].prec = -1;
+ corr[0][i].coef[0] = 0;
+ corr[0][i].coef[1] = 0;
+ corr[0][i].coef[2] = (104 << 14);
+ corr[0][i].coef[3] = (104 << 14);
+ }
+}
+
+/*
+ * js_as_rudder_init_corr() initializes the correction values for
+ * the Panther XL connected rudder.
+ */
+
+static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes)
+{
+ corr[1][0].type = JS_CORR_BROKEN;
+ corr[1][0].prec = 0;
+ corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3);
+ corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3);
+ corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1);
+ corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1);
+}
+
+/*
+ * js_as_probe() probes for A3D joysticks.
+ */
+
+static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port)
+{
+ struct js_as_info iniinfo;
+ struct js_as_info *info = &iniinfo;
+ char *name;
+ char data[JS_AS_MAX_LENGTH];
+ unsigned char u;
+ int i;
+ int numdev;
+
+ memset(info, 0, sizeof(struct js_as_info));
+
+ if (io < 0) return port;
+
+ 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 (js_as_read_packet(io, 1, data) != 1) return port;
+
+ if (data[0] && data[0] <= 4) {
+ info->mode = data[0];
+ info->io = io;
+ request_region(io, 1, "joystick (assasin)");
+ port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read);
+ info = port->info;
+ } else {
+ printk(KERN_WARNING "joy-assasin: unknown joystick device detected "
+ "(io=%#x, id=%d), contact <vojtech@ucw.cz>\n", io, data[0]);
+ return port;
+ }
+
+ udelay(JS_AS_MAX_TIME);
+
+ if (info->mode == JS_AS_MODE_PXL) {
+ printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n",
+ js_register_device(port, 0, 9, 9, "MadCatz Panther XL", js_as_open, js_as_close),
+ info->io);
+ js_as_read(port->info, port->axes, port->buttons);
+ js_as_pxl_init_corr(port->corr, port->axes);
+ if (info->an.axes[0] < 254) {
+ printk(KERN_INFO "js%d: Analog rudder on MadCatz Panther XL\n",
+ js_register_device(port, 1, 1, 0, "Analog rudder", js_as_open, js_as_close));
+ info->rudder = 1;
+ port->axes[1][0] = info->an.axes[0];
+ js_as_rudder_init_corr(port->corr, port->axes);
+ }
+ return port;
+ }
+
+ switch (info->mode) {
+ case JS_AS_MODE_A3D: name = "FP-Gaming Assasin 3D"; break;
+ case JS_AS_MODE_PAN: name = "MadCatz Panther"; break;
+ case JS_AS_MODE_OEM: name = "OEM Assasin 3D"; break;
+ default: name = "This cannot happen"; break;
+ }
+
+ printk(KERN_INFO "js%d: %s at %#x\n",
+ js_register_device(port, 0, 2, 3, name, js_as_open, js_as_close),
+ name, info->io);
+
+ js_as_as_init_corr(port->corr);
+
+ js_as_read(port->info, port->axes, port->buttons);
+
+ for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 254) u |= 1 << i;
+
+ if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0)
+ return port;
+
+ for (i = 0; i < numdev; i++)
+ printk(KERN_INFO "js%d: %s on %s\n",
+ js_register_device(port, i + 1, js_an_axes(i, &info->an), js_an_buttons(i, &info->an),
+ js_an_name(i, &info->an), js_as_open, js_as_close),
+ js_an_name(i, &info->an), name);
+
+ js_an_decode(&info->an, port->axes + 1, port->buttons + 1);
+ js_an_init_corr(&info->an, port->axes + 1, port->corr + 1, 0);
+
+ return port;
+}
+
+#ifndef MODULE
+void __init js_as_setup(char *str, int *ints)
+{
+ int i;
+ for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1];
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_as_init(void)
+#endif
+{
+ int i;
+
+ if (js_as[0] >= 0) {
+ for (i = 0; (js_as[i*3] >= 0) && i < 8; i++)
+ js_as_port = js_as_probe(js_as[i*3], js_as[i*3+1], js_as[i*3+2], js_as_port);
+ } else {
+ for (i = 0; js_as_port_list[i]; i++) js_as_port = js_as_probe(js_as_port_list[i], 0, 0, js_as_port);
+ }
+ if (js_as_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-assasin: no joysticks found\n");
+#endif
+
+ return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ int i;
+ struct js_as_info *info;
+
+ while (js_as_port != NULL) {
+ for (i = 0; i < js_as_port->ndevs; i++)
+ if (js_as_port->devs[i] != NULL)
+ js_unregister_device(js_as_port->devs[i]);
+ info = js_as_port->info;
+ release_region(info->io, 1);
+ js_as_port = js_unregister_port(js_as_port);
+ }
+
+}
+#endif
diff --git a/drivers/char/joystick/joy-console.c b/drivers/char/joystick/joy-console.c
new file mode 100644
index 000000000..ad4be3076
--- /dev/null
+++ b/drivers/char/joystick/joy-console.c
@@ -0,0 +1,596 @@
+/*
+ * joy-console.c Version 0.11V
+ *
+ * Copyright (c) 1998 Andree Borrmann
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * console (NES, SNES, Multi1, Multi2, PSX) gamepads connected
+ * via parallel port. Up to five such controllers can be
+ * connected to one parallel port.
+ */
+
+/*
+ * 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
+ */
+
+#include <asm/io.h>
+#include <asm/system.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>
+#include <linux/delay.h>
+
+
+MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>");
+MODULE_PARM(js_console, "2-6i");
+MODULE_PARM(js_console2,"2-6i");
+MODULE_PARM(js_console3,"2-6i");
+
+
+#define JS_NO_PAD 0
+#define JS_SNES_PAD 1
+#define JS_NES_PAD 2
+#define JS_NES4_PAD 3
+#define JS_MULTI_STICK 4
+#define JS_MULTI2_STICK 5
+#define JS_PSX_PAD 6
+
+#define JS_MAX_PAD JS_PSX_PAD
+
+struct js_console_info {
+#ifdef USE_PARPORT
+ struct pardevice *port; /* parport device */
+#else
+ int port; /* hw port */
+#endif
+ int pads; /* total number of pads */
+ int snes; /* SNES pads */
+ int nes; /* NES pads */
+ int multi; /* Multi joysticks */
+ int multi2; /* Multi joysticks with 2 buttons */
+ int psx; /* Normal PSX controllers */
+ int negcon; /* PSX NEGCON controllers */
+};
+
+static struct js_port* js_console_port = NULL;
+
+static int js_console[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int js_console2[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int js_console3[] __initdata = { -1, 0, 0, 0, 0, 0 };
+
+static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
+
+/*
+ * NES/SNES support.
+ */
+
+#define JS_NES_DELAY 6 /* Delay between bits - 6us */
+
+#define JS_NES_LENGTH 8 /* The NES pads use 8 bits of data */
+
+#define JS_NES_A 0
+#define JS_NES_B 1
+#define JS_NES_START 2
+#define JS_NES_SELECT 3
+#define JS_NES_UP 4
+#define JS_NES_DOWN 5
+#define JS_NES_LEFT 6
+#define JS_NES_RIGHT 7
+
+#define JS_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */
+
+#define JS_SNES_B 0
+#define JS_SNES_Y 1
+#define JS_SNES_START 2
+#define JS_SNES_SELECT 3
+#define JS_SNES_UP 4
+#define JS_SNES_DOWN 5
+#define JS_SNES_LEFT 6
+#define JS_SNES_RIGHT 7
+#define JS_SNES_A 8
+#define JS_SNES_X 9
+#define JS_SNES_L 10
+#define JS_SNES_R 11
+
+#define JS_NES_POWER 0xf8
+#define JS_NES_CLOCK 0x01
+#define JS_NES_LATCH 0x02
+
+/*
+ * js_nes_read_packet() reads a NES/SNES packet.
+ * Each pad uses one bit per byte. So all pads connected to
+ * this port are read in parallel.
+ */
+
+static void js_nes_read_packet(struct js_console_info *info, int length, unsigned char *data)
+{
+ int i;
+
+ JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK + JS_NES_LATCH, info->port);
+ udelay(JS_NES_DELAY * 2);
+ JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port);
+
+ for (i = 0; i < length; i++) {
+ udelay(JS_NES_DELAY);
+ JS_PAR_DATA_OUT(JS_NES_POWER, info->port);
+ data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT;
+ udelay(JS_NES_DELAY);
+ JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port);
+ }
+}
+
+/*
+ * Multisystem joystick support
+ */
+
+#define JS_MULTI_LENGTH 5 /* Multi system joystick packet lenght is 5 */
+#define JS_MULTI2_LENGTH 6 /* One more bit for one more button */
+
+#define JS_MULTI_UP 0
+#define JS_MULTI_DOWN 1
+#define JS_MULTI_LEFT 2
+#define JS_MULTI_RIGHT 3
+#define JS_MULTI_BUTTON 4
+#define JS_MULTI_BUTTON2 5
+
+/*
+ * js_multi_read_packet() reads a Multisystem joystick packet.
+ */
+
+static void js_multi_read_packet(struct js_console_info *info, int length, unsigned char *data)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ JS_PAR_DATA_OUT(~(1 << i), info->port);
+ data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT;
+ }
+}
+
+/*
+ * PSX support
+ */
+
+#define JS_PSX_DELAY 10
+
+#define JS_PSX_LENGTH 8
+
+#define JS_PSX_NORMAL 0x41
+#define JS_PSX_NEGCON 0x23
+#define JS_PSX_MOUSE 0x12
+
+#define JS_PSX_SELBUT 0x01
+#define JS_PSX_START 0x08
+#define JS_PSX_UP 0x10
+#define JS_PSX_RIGHT 0x20
+#define JS_PSX_DOWN 0x40
+#define JS_PSX_LEFT 0x80
+
+#define JS_PSX_CLOCK 0x01
+#define JS_PSX_COMMAND 0x02
+#define JS_PSX_POWER 0xf8
+#define JS_PSX_NOPOWER 0x04
+#define JS_PSX_SELECT 0x08
+
+#define JS_PSX_CTRL_OUT(X,Y) JS_PAR_CTRL_OUT((X)^0x0f, Y)
+
+/*
+ * js_psx_command() writes 8bit command and reads 8bit data from
+ * the psx pad.
+ */
+
+static int js_psx_command(struct js_console_info *info, int b)
+{
+ int i, cmd, ret=0;
+
+ cmd = (b&1)?JS_PSX_COMMAND:0;
+ for (i=0; i<8; i++) {
+ JS_PSX_CTRL_OUT(cmd, info->port);
+ udelay(JS_PSX_DELAY);
+ ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<<i) : 0;
+ cmd = (b&1)?JS_PSX_COMMAND:0;
+ JS_PSX_CTRL_OUT(JS_PSX_CLOCK | cmd, info->port);
+ udelay(JS_PSX_DELAY);
+ b >>= 1;
+ }
+ return ret;
+}
+
+/*
+ * js_psx_read_packet() reads a whole psx packet and returns
+ * device identifier code.
+ */
+
+static int js_psx_read_packet(struct js_console_info *info, int length, unsigned char *data)
+{
+ int i, ret;
+ unsigned long flags;
+
+ __save_flags(flags);
+ __cli();
+
+ JS_PAR_DATA_OUT(JS_PSX_POWER, info->port);
+
+ JS_PSX_CTRL_OUT(JS_PSX_CLOCK | JS_PSX_SELECT, info->port); /* Select pad */
+ udelay(JS_PSX_DELAY*2);
+ js_psx_command(info, 0x01); /* Access pad */
+ ret = js_psx_command(info, 0x42); /* Get device id */
+ if (js_psx_command(info, 0)=='Z') /* okay? */
+ for (i=0; i<length; i++)
+ data[i]=js_psx_command(info, 0);
+ else ret = -1;
+
+ JS_PSX_CTRL_OUT(JS_PSX_SELECT | JS_PSX_CLOCK, info->port);
+ __restore_flags(flags);
+
+ return ret;
+}
+
+
+/*
+ * js_console_read() reads and analyzes console pads data.
+ */
+
+#define JS_MAX_LENGTH JS_SNES_LENGTH
+
+static int js_console_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_console_info *info = xinfo;
+ unsigned char data[JS_MAX_LENGTH];
+
+ int i, s;
+ int n = 0;
+
+/*
+ * NES and SNES pads
+ */
+
+ if (info->nes || info->snes) {
+
+ js_nes_read_packet(info, info->snes ? JS_SNES_LENGTH : JS_NES_LENGTH, data);
+
+ for (i = 0; i < 5; i++) {
+ s = status_bit[i];
+ if (info->nes & s) {
+ axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0);
+ axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0);
+
+ buttons[n][0] = ((data[JS_NES_A] &s)?1:0) | ((data[JS_NES_B] &s)?2:0)
+ | ((data[JS_NES_START]&s)?4:0) | ((data[JS_NES_SELECT]&s)?8:0);
+
+ n++;
+ } else
+ if (info->snes & s) {
+ axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0);
+ axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0);
+
+ buttons[n][0] = ((data[JS_SNES_A] &s)?0x01:0) | ((data[JS_SNES_B] &s)?0x02:0)
+ | ((data[JS_SNES_X] &s)?0x04:0) | ((data[JS_SNES_Y] &s)?0x08:0)
+ | ((data[JS_SNES_L] &s)?0x10:0) | ((data[JS_SNES_R] &s)?0x20:0)
+ | ((data[JS_SNES_START]&s)?0x40:0) | ((data[JS_SNES_SELECT]&s)?0x80:0);
+ n++;
+ }
+ }
+ }
+
+/*
+ * Multi and Multi2 joysticks
+ */
+
+ if (info->multi || info->multi2) {
+
+ js_multi_read_packet(info, info->multi2 ? JS_MULTI2_LENGTH : JS_MULTI_LENGTH, data);
+
+ for (i = 0; i < 5; i++) {
+ s = status_bit[i];
+ if (info->multi & s) {
+ axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0);
+ axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0);
+
+ buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0;
+
+ n++;
+ } else
+ if (info->multi2 & s) {
+ axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0);
+ axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0);
+
+ buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0 | (data[JS_MULTI_BUTTON2]&s)?2:0;
+
+ n++;
+ }
+ }
+ }
+
+/*
+ * PSX controllers
+ */
+
+ if (info->psx && (js_psx_read_packet(info, 2, data) == JS_PSX_NORMAL)) { /* FIXME? >1 PSX pads? */
+
+ axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1);
+ axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1);
+
+ buttons[n][0] = ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) |
+ (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100);
+
+ n++;
+ }
+
+ return -(n != info->pads);
+}
+
+/*
+ * open callback: claim parport.
+ */
+
+int js_console_open(struct js_dev *dev)
+{
+#ifdef USE_PARPORT
+ struct js_console_info *info = dev->port->info;
+ if (!MOD_IN_USE && parport_claim(info->port)) return -EBUSY;
+#endif
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * close callback: release parport
+ */
+
+int js_console_close(struct js_dev *dev)
+{
+#ifdef USE_PARPORT
+ struct js_console_info *info = dev->port->info;
+#endif
+ MOD_DEC_USE_COUNT;
+#ifdef USE_PARPORT
+ if (!MOD_IN_USE) parport_release(info->port);
+#endif
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ struct js_console_info *info;
+ int i;
+
+ while (js_console_port != NULL) {
+ for (i = 0; i < js_console_port->ndevs; i++)
+ if (js_console_port->devs[i] != NULL)
+ js_unregister_device(js_console_port->devs[i]);
+ info = js_console_port->info;
+#ifdef USE_PARPORT
+ parport_unregister_device(info->port);
+#else
+ release_region(info->port, 3);
+#endif
+ js_console_port = js_unregister_port(js_console_port);
+ }
+}
+#endif
+
+/*
+ * js_console_init_corr() initializes correction values of
+ * console gamepads.
+ */
+
+static void __init js_console_init_corr(int num_axes, struct js_corr *corr)
+{
+ int i;
+
+ for (i = 0; i < num_axes; i++) {
+ corr[i].type = JS_CORR_BROKEN;
+ corr[i].prec = 0;
+ corr[i].coef[0] = 0;
+ corr[i].coef[1] = 0;
+ corr[i].coef[2] = (1 << 29);
+ corr[i].coef[3] = (1 << 29);
+ }
+}
+
+/*
+ * js_console_probe() probes for console gamepads.
+ * Only PSX pads can really be probed for.
+ */
+
+static struct js_port __init *js_console_probe(int *config, struct js_port *port)
+{
+ char *name[5];
+ int i, psx, axes[5], buttons[5];
+ unsigned char data[2]; /* used for PSX probe */
+ struct js_console_info info;
+
+ memset(&info, 0, sizeof(struct js_console_info));
+
+ if (config[0] < 0) return port;
+
+#ifdef USE_PARPORT
+ {
+ struct parport *pp;
+
+ if (config[0] > 0x10)
+ for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next);
+ else
+ for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--;
+
+ if (pp == NULL) {
+ printk(KERN_ERR "joy-console: no such parport\n");
+ return port;
+ }
+
+ info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+ if (!info.port)
+ return port;
+ }
+
+ if (parport_claim(info.port))
+ {
+ parport_unregister_device(info.port); /* port currently not available ... */
+ return port;
+ }
+#else
+ info.port = config[0];
+ if (check_region(info.port, 3)) return port;
+ request_region(info.port, 3, "joystick (console pad)");
+#endif
+
+ for (i = 0; i < 5; i++)
+ switch(config[i+1]) {
+
+ case JS_NO_PAD:
+
+ break;
+
+ case JS_SNES_PAD:
+
+ axes[info.pads] = 2;
+ buttons[info.pads] = 8;
+ name[info.pads] = "SNES pad";
+ info.snes |= status_bit[i];
+ info.pads++;
+ break;
+
+ case JS_NES_PAD:
+
+ axes[info.pads] = 2;
+ buttons[info.pads] = 4;
+ name[info.pads] = "NES pad";
+ info.nes |= status_bit[i];
+ info.pads++;
+ break;
+
+ case JS_MULTI_STICK:
+
+ axes[info.pads] = 2;
+ buttons[info.pads] = 1;
+ name[info.pads] = "Multisystem joystick";
+ info.multi |= status_bit[i];
+ info.pads++;
+ break;
+
+ case JS_MULTI2_STICK:
+
+ axes[info.pads] = 2;
+ buttons[info.pads] = 2;
+ name[info.pads] = "Multisystem joystick (2 fire)";
+ info.multi |= status_bit[i];
+ info.pads++;
+ break;
+
+ case JS_PSX_PAD:
+
+ info.psx |= status_bit[i];
+ psx = js_psx_read_packet(&info, 2, data);
+ psx = js_psx_read_packet(&info, 2, data);
+ info.psx &= ~status_bit[i];
+
+ switch(psx) {
+ case JS_PSX_NORMAL:
+ axes[info.pads] = 2;
+ buttons[info.pads] = 10;
+ name[info.pads] = "PSX controller";
+ info.psx |= status_bit[i];
+ info.pads++;
+ break;
+ case JS_PSX_NEGCON:
+ printk(KERN_WARNING "joy-console: NegCon not yet supported...\n");
+ break;
+ case JS_PSX_MOUSE:
+ printk(KERN_WARNING "joy-console: PSX mouse not supported...\n");
+ break;
+ case -1:
+ printk(KERN_ERR "joy-console: no PSX controller found...\n");
+ break;
+ default:
+ printk(KERN_WARNING "joy-console: unknown PSX controller 0x%x\n", psx);
+ }
+ break;
+
+ default:
+
+ printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]);
+ }
+
+ if (!info.pads) {
+#ifdef USE_PARPORT
+ parport_release(info.port);
+ parport_unregister_device(info.port);
+#else
+ release_region(info.port, 3);
+#endif
+ return port;
+ }
+
+ port = js_register_port(port, &info, info.pads, sizeof(struct js_console_info), js_console_read);
+
+ for (i = 0; i < info.pads; i++) {
+#ifdef USE_PARPORT
+ printk(KERN_INFO "js%d: %s on %s\n",
+ js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close),
+ name[i], info.port->port->name);
+#else
+ printk(KERN_INFO "js%d: %s at %#x\n",
+ js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close),
+ name[i], info.port);
+#endif
+
+ js_console_init_corr(axes[i], port->corr[i]);
+ }
+
+#ifdef USE_PARPORT
+ parport_release(info.port);
+#endif
+ return port;
+}
+
+#ifndef MODULE
+void __init js_console_setup(char *str, int *ints)
+{
+ int i;
+
+ if (!strcmp(str,"js_console"))
+ for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1];
+ if (!strcmp(str,"js_console2"))
+ for (i = 0; i <= ints[0] && i < 6; i++) js_console2[i] = ints[i+1];
+ if (!strcmp(str,"js_console3"))
+ for (i = 0; i <= ints[0] && i < 6; i++) js_console3[i] = ints[i+1];
+
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_console_init(void)
+#endif
+{
+ js_console_port = js_console_probe(js_console, js_console_port);
+ js_console_port = js_console_probe(js_console2, js_console_port);
+ js_console_port = js_console_probe(js_console3, js_console_port);
+
+ if (js_console_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-console: no joysticks specified\n");
+#endif
+ return -ENODEV;
+}
diff --git a/drivers/char/joystick/joy-db9.c b/drivers/char/joystick/joy-db9.c
new file mode 100644
index 000000000..82a5c6a0f
--- /dev/null
+++ b/drivers/char/joystick/joy-db9.c
@@ -0,0 +1,406 @@
+/*
+ * joy-db9.c Version 0.5V
+ *
+ * Copyright (c) 1998 Andree Borrmann
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * console (Atari, Amstrad, Commodore, Amiga, Sega) joysticks
+ * and gamepads connected to the parallel port.
+ */
+
+/*
+ * 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
+ */
+
+#include <asm/io.h>
+#include <asm/system.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>
+#include <linux/delay.h>
+
+MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>");
+MODULE_PARM(js_db9, "2i");
+MODULE_PARM(js_db9_2, "2i");
+MODULE_PARM(js_db9_3, "2i");
+
+#define JS_MULTI_STICK 0x01
+#define JS_MULTI2_STICK 0x02
+#define JS_GENESIS_PAD 0x03
+#define JS_GENESIS5_PAD 0x05
+#define JS_GENESIS6_PAD 0x06
+#define JS_SATURN_PAD 0x07
+#define JS_MULTI_0802 0x08
+#define JS_MAX_PAD 0x09
+
+#define JS_DB9_UP 0x01
+#define JS_DB9_DOWN 0x02
+#define JS_DB9_LEFT 0x04
+#define JS_DB9_RIGHT 0x08
+#define JS_DB9_FIRE1 0x10
+#define JS_DB9_FIRE2 0x20
+#define JS_DB9_FIRE3 0x40
+#define JS_DB9_FIRE4 0x80
+
+#define JS_DB9_NORMAL 0x22
+#define JS_DB9_NOSELECT 0x20
+
+#define JS_DB9_SATURN0 0x20
+#define JS_DB9_SATURN1 0x22
+#define JS_DB9_SATURN2 0x24
+#define JS_DB9_SATURN3 0x26
+
+#define JS_GENESIS6_DELAY 14
+
+static struct js_port* js_db9_port = NULL;
+
+static int js_db9[] __initdata = { -1, 0 };
+static int js_db9_2[] __initdata = { -1, 0 };
+static int js_db9_3[] __initdata = { -1, 0 };
+
+struct js_db9_info {
+#ifdef USE_PARPORT
+ struct pardevice *port; /* parport device */
+#else
+ int port; /* hw port */
+#endif
+ int mode; /* pad mode */
+};
+
+/*
+ * js_db9_read() reads and analyzes db9 joystick data.
+ */
+
+static int js_db9_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_db9_info *info = xinfo;
+ int data;
+
+ switch(info->mode)
+ {
+ case JS_MULTI_0802:
+
+ data = JS_PAR_STATUS(info->port) >> 3;
+
+ axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1);
+ axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+ buttons[0][0] = (data&JS_DB9_FIRE1?1:0);
+
+ break;
+
+ case JS_MULTI_STICK:
+
+ data = JS_PAR_DATA_IN(info->port);
+
+ axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1);
+ axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+ buttons[0][0] = (data&JS_DB9_FIRE1?0:1);
+
+ break;
+
+ case JS_MULTI2_STICK:
+
+ data=JS_PAR_DATA_IN(info->port);
+
+ axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1);
+ axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+ buttons[0][0] = (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:2);
+
+ break;
+
+ case JS_GENESIS_PAD:
+
+ JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port);
+ data = JS_PAR_DATA_IN(info->port);
+
+ axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1);
+ axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+ buttons[0][0] = (data&JS_DB9_FIRE1?0:2) | (data&JS_DB9_FIRE2?0:4);
+
+ JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port);
+ data=JS_PAR_DATA_IN(info->port);
+
+ buttons[0][0] |= (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:8);
+
+ break;
+
+ case JS_GENESIS5_PAD:
+
+ JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port);
+ data=JS_PAR_DATA_IN(info->port);
+
+ axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1);
+ axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+ buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04);
+
+ JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+ data=JS_PAR_DATA_IN(info->port);
+
+ buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08) |
+ (data&JS_DB9_LEFT ?0:0x10) | (data&JS_DB9_RIGHT?0:0x20);
+ break;
+
+ case JS_GENESIS6_PAD:
+
+ JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); /* 1 */
+ udelay(JS_GENESIS6_DELAY);
+ data=JS_PAR_DATA_IN(info->port);
+
+ axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1);
+ axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+ buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04);
+
+ JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+ udelay(JS_GENESIS6_DELAY);
+ data=JS_PAR_DATA_IN(info->port);
+
+ buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08);
+
+ JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 2 */
+ udelay(JS_GENESIS6_DELAY);
+ JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+ udelay(JS_GENESIS6_DELAY);
+ JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */
+ udelay(JS_GENESIS6_DELAY);
+ JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+ data=JS_PAR_DATA_IN(info->port);
+
+ buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN?0:0x20) | (data&JS_DB9_UP?0:0x40);
+
+ udelay(JS_GENESIS6_DELAY);
+ JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 4 */
+ udelay(JS_GENESIS6_DELAY);
+ JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+
+ break;
+
+ case JS_SATURN_PAD:
+
+ JS_PAR_CTRL_OUT(JS_DB9_SATURN0, info->port);
+ data = JS_PAR_DATA_IN(info->port);
+
+ buttons[0][0] = (data&JS_DB9_UP ?0:0x20) | (data&JS_DB9_DOWN ?0:0x10) |
+ (data&JS_DB9_LEFT?0:0x08) | (data&JS_DB9_RIGHT?0:0x40);
+
+ JS_PAR_CTRL_OUT(JS_DB9_SATURN2, info->port);
+ data = JS_PAR_DATA_IN(info->port);
+
+ axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1);
+ axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+ JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+ data = JS_PAR_DATA_IN(info->port);
+
+ buttons[0][0] |= (data&JS_DB9_UP ?0:0x02) | (data&JS_DB9_DOWN ?0:0x04) |
+ (data&JS_DB9_LEFT?0:0x01) | (data&JS_DB9_RIGHT?0:0x80);
+
+
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * open callback: claim parport.
+ */
+
+int js_db9_open(struct js_dev *dev)
+{
+ struct js_db9_info *info = dev->port->info;
+
+ if (!MOD_IN_USE) {
+#ifdef USE_PARPORT
+ if (parport_claim(info->port)) return -EBUSY;
+#endif
+
+ JS_PAR_ECTRL_OUT(0x35,info->port); /* enable PS/2 mode: */
+ JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); /* reverse direction, enable Select signal */
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * close callback: release parport
+ */
+
+int js_db9_close(struct js_dev *dev)
+{
+ struct js_db9_info *info = dev->port->info;
+
+ MOD_DEC_USE_COUNT;
+
+ if (!MOD_IN_USE) {
+
+ JS_PAR_CTRL_OUT(0x00,info->port); /* normal direction */
+ JS_PAR_ECTRL_OUT(0x15,info->port); /* enable normal mode */
+
+#ifdef USE_PARPORT
+ parport_release(info->port);
+#endif
+ }
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ struct js_db9_info *info;
+
+ while (js_db9_port != NULL) {
+ js_unregister_device(js_db9_port->devs[0]);
+ info = js_db9_port->info;
+#ifdef USE_PARPORT
+ parport_unregister_device(info->port);
+#else
+ release_region(info->port, 3);
+ release_region(info->port+0x402, 1);
+#endif
+ js_db9_port = js_unregister_port(js_db9_port);
+ }
+
+}
+#endif
+
+/*
+ * js_db9_init_corr() initializes correction values of
+ * db9 gamepads.
+ */
+
+static void __init js_db9_init_corr(struct js_corr **corr)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ corr[0][i].type = JS_CORR_BROKEN;
+ corr[0][i].prec = 0;
+ corr[0][i].coef[0] = 0;
+ corr[0][i].coef[1] = 0;
+ corr[0][i].coef[2] = (1 << 29);
+ corr[0][i].coef[3] = (1 << 29);
+ }
+}
+
+/*
+ * js_db9_probe() probes for db9 gamepads.
+ */
+
+static struct js_port __init *js_db9_probe(int *config, struct js_port *port)
+{
+ struct js_db9_info info;
+ char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,7,8,1};
+ char *name[JS_MAX_PAD] = {NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad",
+ NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick"};
+
+ if (config[0] < 0) return port;
+ if (config[1] < 0 || config[1] >= JS_MAX_PAD || !name[config[1]]) return port;
+
+#ifdef USE_PARPORT
+ {
+ struct parport *pp;
+
+ if (config[0] > 0x10)
+ for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next);
+ else
+ for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--;
+
+ if (pp == NULL) {
+ printk(KERN_ERR "joy-db9: no such parport\n");
+ return port;
+ }
+
+ if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2))) {
+ printk(KERN_ERR "js-db9: specified parport is not bidirectional\n");
+ return port;
+ }
+
+ info.port = parport_register_device(pp, "joystick (db9)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+ if (!info.port)
+ return port;
+ }
+#else
+ info.port = config[0];
+ if (check_region(info.port, 3) || check_region(info.port+0x402,1)) return port;
+ request_region(info.port, 3, "joystick (db9)");
+ request_region(info.port+0x402, 1, "joystick (db9)");
+#endif
+
+ info.mode = config[1];
+
+ port = js_register_port(port, &info, 1, sizeof(struct js_db9_info), js_db9_read);
+
+#ifdef USE_PARPORT
+ printk(KERN_INFO "js%d: %s on %s\n",
+ js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close),
+ name[info.mode], info.port->port->name);
+#else
+ printk(KERN_INFO "js%d: %s at %#x\n",
+ js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close),
+ name[info.mode], info.port);
+#endif
+
+ js_db9_init_corr(port->corr);
+
+ return port;
+}
+
+#ifndef MODULE
+void __init js_db9_setup(char *str, int *ints)
+{
+ int i;
+
+ if (!strcmp(str,"js_db9"))
+ for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1];
+ if (!strcmp(str,"js_db9_2"))
+ for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1];
+ if (!strcmp(str,"js_db9_3"))
+ for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1];
+
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_db9_init(void)
+#endif
+{
+ js_db9_port = js_db9_probe(js_db9, js_db9_port);
+ js_db9_port = js_db9_probe(js_db9_2, js_db9_port);
+ js_db9_port = js_db9_probe(js_db9_3, js_db9_port);
+
+ if (js_db9_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-db9: no joysticks specified\n");
+#endif
+ return -ENODEV;
+}
diff --git a/drivers/char/joystick/joy-gravis.c b/drivers/char/joystick/joy-gravis.c
new file mode 100644
index 000000000..9185d3113
--- /dev/null
+++ b/drivers/char/joystick/joy-gravis.c
@@ -0,0 +1,401 @@
+/*
+ * joy-gravis.c Version 1.2
+ *
+ * Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Gravis GrIP 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/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define JS_GR_MODE_GPP 1
+#define JS_GR_LENGTH_GPP 24
+#define JS_GR_STROBE_GPP 75
+
+#define JS_GR_MODE_XT 2
+#define JS_GR_MODE_BD 3
+#define JS_GR_LENGTH_XT 4
+#define JS_GR_STROBE_XT 30
+#define JS_GR_MAX_CHUNKS_XT 10
+#define JS_GR_MAX_BITS_XT 30
+
+static int js_gr_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_gr_port __initdata = NULL;
+
+struct js_gr_info {
+ int io;
+ unsigned char mode[2];
+};
+
+/*
+ * js_gr_gpp_read_packet() reads a Gravis GamePad Pro packet.
+ */
+
+static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data)
+{
+ unsigned int t, t1;
+ unsigned char u, v;
+ int i;
+ unsigned long flags;
+
+ int strobe = (js_time_speed * JS_GR_STROBE_GPP) >> 10;
+
+ i = 0;
+ data[0] = 0;
+
+ __save_flags(flags);
+ __cli();
+ u = inb(io) >> shift;
+ t = js_get_time();
+
+ do {
+ v = (inb(io) >> shift) & 3;
+ t1 = js_get_time();
+ if ((u ^ v) & u & 1) {
+ data[0] |= (v >> 1) << i++;
+ t = t1;
+ }
+ u = v;
+ } while (i < JS_GR_LENGTH_GPP && js_delta(t1,t) < strobe);
+
+ __restore_flags(flags);
+
+ if (i < JS_GR_LENGTH_GPP) return -1;
+
+ for (i = 0; i < JS_GR_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++)
+ data[0] = data[0] >> 1 | (data[0] & 1) << 23;
+
+ return -(i == JS_GR_LENGTH_GPP);
+}
+
+/*
+ * js_gr_xt_read_packet() reads a Gravis Xterminator packet.
+ */
+
+static int js_gr_xt_read_packet(int io, int shift, unsigned int *data)
+{
+ unsigned int t, t1;
+ unsigned char u, v, w;
+ unsigned int i, j, buf, crc;
+ unsigned long flags;
+ char status;
+
+ int strobe = (js_time_speed * JS_GR_STROBE_XT) >> 10;
+
+ data[0] = data[1] = data[2] = data[3] = 0;
+ status = buf = i = j = 0;
+
+ __save_flags(flags);
+ __cli();
+
+ v = w = (inb(io) >> shift) & 3;
+ t = js_get_time();
+
+ do {
+ u = (inb(io) >> shift) & 3;
+ t1 = js_get_time();
+
+ if (u ^ v) {
+
+ if ((u ^ v) & 1) {
+ buf = (buf << 1) | (u >> 1);
+ i++;
+ } else
+
+ if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) {
+ if (i == 20) {
+ crc = buf ^ (buf >> 7) ^ (buf >> 14);
+ if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) {
+ data[buf >> 18] = buf >> 4;
+ status |= 1 << (buf >> 18);
+ }
+ j++;
+ }
+ buf = 0;
+ i = 0;
+ }
+
+ t = t1;
+ w = v;
+ v = u;
+ }
+
+ } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && js_delta(t1,t) < strobe);
+
+ __restore_flags(flags);
+
+ return -(status != 0xf);
+}
+
+/*
+ * js_gr_read() reads and analyzes GrIP joystick data.
+ */
+
+static int js_gr_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_gr_info *info = xinfo;
+ unsigned int data[JS_GR_LENGTH_XT];
+ int i;
+
+ for (i = 0; i < 2; i++)
+ switch (info->mode[i]) {
+
+ case JS_GR_MODE_GPP:
+
+ if (js_gr_gpp_read_packet(info->io, (i << 1) + 4, data)) return -1;
+
+ axes[i][0] = ((data[0] >> 15) & 1) - ((data[0] >> 16) & 1);
+ axes[i][1] = ((data[0] >> 13) & 1) - ((data[0] >> 12) & 1);
+
+ data[0] = ((data[0] >> 6) & 0x37) | (data[0] & 0x08) | ((data[0] << 1) & 0x40) |
+ ((data[0] << 5) & 0x80) | ((data[0] << 8) & 0x300);
+
+ buttons[i][0] = (data[0] & 0xfc) | ((data[0] >> 1) & 0x101) | ((data[0] << 1) & 0x202);
+
+ break;
+
+ case JS_GR_MODE_XT:
+
+ if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1;
+
+ axes[i][0] = (data[0] >> 2) & 0x3f;
+ axes[i][1] = 63 - ((data[0] >> 8) & 0x3f);
+ axes[i][2] = (data[1] >> 2) & 0x3f;
+ axes[i][3] = (data[1] >> 8) & 0x3f;
+ axes[i][4] = (data[2] >> 8) & 0x3f;
+
+ axes[i][5] = ((data[2] >> 1) & 1) - ( data[2] & 1);
+ axes[i][6] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1);
+ axes[i][7] = ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1);
+ axes[i][8] = ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1);
+
+ buttons[i][0] = (data[3] >> 3) & 0x7ff;
+
+ break;
+
+ case JS_GR_MODE_BD:
+
+ if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1;
+
+ axes[i][0] = (data[0] >> 2) & 0x3f;
+ axes[i][1] = 63 - ((data[0] >> 8) & 0x3f);
+ axes[i][2] = (data[2] >> 8) & 0x3f;
+
+ axes[i][3] = ((data[2] >> 1) & 1) - ( data[2] & 1);
+ axes[i][4] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1);
+
+ buttons[i][0] = ((data[3] >> 6) & 0x01) | ((data[3] >> 3) & 0x06)
+ | ((data[3] >> 4) & 0x18);
+
+ break;
+
+ default:
+ break;
+
+ }
+
+
+ return 0;
+}
+
+/*
+ * js_gr_open() is a callback from the file open routine.
+ */
+
+static int js_gr_open(struct js_dev *jd)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_gr_close() is a callback from the file release routine.
+ */
+
+static int js_gr_close(struct js_dev *jd)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_gr_init_corr() initializes correction values of
+ * GrIP joysticks.
+ */
+
+static void __init js_gr_init_corr(int mode, struct js_corr *corr)
+{
+ int i;
+
+ switch (mode) {
+
+ case JS_GR_MODE_GPP:
+
+ for (i = 0; i < 2; i++) {
+ corr[i].type = JS_CORR_BROKEN;
+ corr[i].prec = 0;
+ corr[i].coef[0] = 0;
+ corr[i].coef[1] = 0;
+ corr[i].coef[2] = (1 << 29);
+ corr[i].coef[3] = (1 << 29);
+ }
+
+ break;
+
+ case JS_GR_MODE_XT:
+
+ for (i = 0; i < 5; i++) {
+ corr[i].type = JS_CORR_BROKEN;
+ corr[i].prec = 0;
+ corr[i].coef[0] = 31 - 4;
+ corr[i].coef[1] = 32 + 4;
+ corr[i].coef[2] = (1 << 29) / (32 - 14);
+ corr[i].coef[3] = (1 << 29) / (32 - 14);
+ }
+
+ for (i = 5; i < 9; i++) {
+ corr[i].type = JS_CORR_BROKEN;
+ corr[i].prec = 0;
+ corr[i].coef[0] = 0;
+ corr[i].coef[1] = 0;
+ corr[i].coef[2] = (1 << 29);
+ corr[i].coef[3] = (1 << 29);
+ }
+
+ break;
+
+ case JS_GR_MODE_BD:
+
+ for (i = 0; i < 3; i++) {
+ corr[i].type = JS_CORR_BROKEN;
+ corr[i].prec = 0;
+ corr[i].coef[0] = 31 - 4;
+ corr[i].coef[1] = 32 + 4;
+ corr[i].coef[2] = (1 << 29) / (32 - 14);
+ corr[i].coef[3] = (1 << 29) / (32 - 14);
+ }
+
+ for (i = 3; i < 5; i++) {
+ corr[i].type = JS_CORR_BROKEN;
+ corr[i].prec = 0;
+ corr[i].coef[0] = 0;
+ corr[i].coef[1] = 0;
+ corr[i].coef[2] = (1 << 29);
+ corr[i].coef[3] = (1 << 29);
+ }
+
+ break;
+
+ }
+}
+
+/*
+ * js_gr_probe() probes fro GrIP joysticks.
+ */
+
+static struct js_port __init *js_gr_probe(int io, struct js_port *port)
+{
+ struct js_gr_info info;
+ char *names[] = { NULL, "Gravis GamePad Pro", "Gravis Xterminator", "Gravis Blackhawk Digital"};
+ char axes[] = { 0, 2, 9, 5};
+ char buttons[] = { 0, 10, 11, 5};
+ unsigned int data[JS_GR_LENGTH_XT];
+ int i;
+
+ if (check_region(io, 1)) return port;
+
+ info.mode[0] = info.mode[1] = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (!js_gr_gpp_read_packet(io, (i << 1) + 4, data)) info.mode[i] = JS_GR_MODE_GPP;
+ if (!js_gr_xt_read_packet(io, (i << 1) + 4, data)) {
+ if ((data[3] & 7) == 7)
+ info.mode[i] = JS_GR_MODE_XT;
+ if ((data[3] & 7) == 0)
+ info.mode[i] = JS_GR_MODE_BD;
+ }
+ }
+
+ if (!info.mode[0] && !info.mode[1]) return port;
+
+ info.io = io;
+
+ request_region(io, 1, "joystick (gravis)");
+ port = js_register_port(port, &info, 2, sizeof(struct js_gr_info), js_gr_read);
+
+ for (i = 0; i < 2; i++)
+ if (info.mode[i]) {
+ printk(KERN_INFO "js%d: %s at %#x\n",
+ js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]],
+ names[info.mode[i]], js_gr_open, js_gr_close),
+ names[info.mode[i]], io);
+ js_gr_init_corr(info.mode[i], port->corr[i]);
+ }
+
+ return port;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_gr_init(void)
+#endif
+{
+ int *p;
+
+ for (p = js_gr_port_list; *p; p++) js_gr_port = js_gr_probe(*p, js_gr_port);
+ if (js_gr_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-gravis: no joysticks found\n");
+#endif
+
+ return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ int i;
+ struct js_gr_info *info;
+
+ while (js_gr_port != NULL) {
+ for (i = 0; i < js_gr_port->ndevs; i++)
+ if (js_gr_port->devs[i] != NULL)
+ js_unregister_device(js_gr_port->devs[i]);
+ info = js_gr_port->info;
+ release_region(info->io, 1);
+ js_gr_port = js_unregister_port(js_gr_port);
+ }
+}
+#endif
diff --git a/drivers/char/joystick/joy-lightning.c b/drivers/char/joystick/joy-lightning.c
new file mode 100644
index 000000000..fc36c3d50
--- /dev/null
+++ b/drivers/char/joystick/joy-lightning.c
@@ -0,0 +1,366 @@
+/*
+ * joy-lightning.c Version 1.2
+ *
+ * Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * PDPI Lightning 4 gamecards and analog joysticks connected
+ * to them.
+ */
+
+/*
+ * 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 <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_L4_PORT 0x201
+#define JS_L4_SELECT_ANALOG 0xa4
+#define JS_L4_SELECT_DIGITAL 0xa5
+#define JS_L4_SELECT_SECONDARY 0xa6
+#define JS_L4_CMD_ID 0x80
+#define JS_L4_CMD_GETCAL 0x92
+#define JS_L4_CMD_SETCAL 0x93
+#define JS_L4_ID 0x04
+#define JS_L4_BUSY 0x01
+#define JS_L4_TIMEOUT 80 /* 80 us */
+
+static struct js_port* __initdata js_l4_port = NULL;
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_l4, "2-24i");
+
+static int js_l4[]={-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};
+
+#include "joy-analog.h"
+
+struct js_l4_info {
+ int port;
+ struct js_an_info an;
+};
+
+/*
+ * js_l4_wait_ready() waits for the L4 to become ready.
+ */
+
+static int js_l4_wait_ready(void)
+{
+ unsigned int t, t1, timeout;
+ timeout = (JS_L4_TIMEOUT * js_time_speed) >> 10;
+ t = t1 = js_get_time();
+ while ((inb(JS_L4_PORT) & JS_L4_BUSY) && (js_delta(t1 = js_get_time(), t) < timeout));
+ return -(js_delta(t1, t) >= timeout);
+}
+
+/*
+ * js_l4_read() reads data from the Lightning 4.
+ */
+
+static int js_l4_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_l4_info *info = xinfo;
+ int i;
+ unsigned char status;
+
+ outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+ outb(JS_L4_SELECT_DIGITAL + (info->port >> 2), JS_L4_PORT);
+
+ if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1;
+ outb(info->port & 3, JS_L4_PORT);
+
+ if (js_l4_wait_ready()) return -1;
+ status = inb(JS_L4_PORT);
+
+ for (i = 0; i < 4; i++)
+ if (status & (1 << i)) {
+ if (js_l4_wait_ready()) return -1;
+ info->an.axes[i] = inb(JS_L4_PORT);
+ }
+
+ if (status & 0x10) {
+ if (js_l4_wait_ready()) return -1;
+ info->an.buttons = inb(JS_L4_PORT);
+ }
+
+ js_an_decode(&info->an, axes, buttons);
+
+ return 0;
+}
+
+/*
+ * js_l4_getcal() reads the L4 with calibration values.
+ */
+
+static int js_l4_getcal(int port, int *cal)
+{
+ int i;
+
+ outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+ outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT);
+
+ if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1;
+ outb(JS_L4_CMD_GETCAL, JS_L4_PORT);
+
+ if (js_l4_wait_ready()) return -1;
+ if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + (port >> 2)) return -1;
+
+ if (js_l4_wait_ready()) return -1;
+ outb(port & 3, JS_L4_PORT);
+
+ for (i = 0; i < 4; i++) {
+ if (js_l4_wait_ready()) return -1;
+ cal[i] = inb(JS_L4_PORT);
+ }
+
+ return 0;
+}
+
+/*
+ * js_l4_setcal() programs the L4 with calibration values.
+ */
+
+static int js_l4_setcal(int port, int *cal)
+{
+ int i;
+
+ outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+ outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT);
+
+ if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1;
+ outb(JS_L4_CMD_SETCAL, JS_L4_PORT);
+
+ if (js_l4_wait_ready()) return -1;
+ if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + (port >> 2)) return -1;
+
+ if (js_l4_wait_ready()) return -1;
+ outb(port & 3, JS_L4_PORT);
+
+ for (i = 0; i < 4; i++) {
+ if (js_l4_wait_ready()) return -1;
+ outb(cal[i], JS_L4_PORT);
+ }
+
+ return 0;
+}
+
+/*
+ * js_l4_calibrate() calibrates the L4 for the attached device, so
+ * that the device's resistance fits into the L4's 8-bit range.
+ */
+
+static void js_l4_calibrate(struct js_l4_info *info)
+{
+ int i;
+ int cal[4];
+ int axes[4];
+ int t;
+
+ js_l4_getcal(info->port, cal);
+
+ for (i = 0; i < 4; i++)
+ axes[i] = info->an.axes[i];
+
+ if ((info->an.extensions & JS_AN_BUTTON_PXY_X) && !(info->an.extensions & JS_AN_BUTTON_PXY_U))
+ axes[2] >>= 1; /* Pad button X */
+
+ if ((info->an.extensions & JS_AN_BUTTON_PXY_Y) && !(info->an.extensions & JS_AN_BUTTON_PXY_V))
+ axes[3] >>= 1; /* Pad button Y */
+
+ if (info->an.extensions & JS_AN_HAT_FCS)
+ axes[3] >>= 1; /* FCS hat */
+
+ if (((info->an.mask[0] & 0xb) == 0xb) || ((info->an.mask[1] & 0xb) == 0xb))
+ axes[3] = (axes[0] + axes[1]) >> 1; /* Throttle */
+
+ for (i = 0; i < 4; i++) {
+ t = (axes[i] * cal[i]) / 100;
+ if (t > 255) t = 255;
+ info->an.axes[i] = (info->an.axes[i] * cal[i]) / t;
+ cal[i] = t;
+ }
+
+ js_l4_setcal(info->port, cal);
+}
+
+/*
+ * js_l4_open() is a callback from the file open routine.
+ */
+
+static int js_l4_open(struct js_dev *jd)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_l4_close() is a callback from the file release routine.
+ */
+
+static int js_l4_close(struct js_dev *jd)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_l4_probe() probes for joysticks on the L4 cards.
+ */
+
+static struct js_port __init *js_l4_probe(unsigned char *cards, int l4port, int mask0, int mask1, struct js_port *port)
+{
+ struct js_l4_info iniinfo;
+ struct js_l4_info *info = &iniinfo;
+ int cal[4] = {255,255,255,255};
+ int i, numdev;
+ unsigned char u;
+
+ if (l4port < 0) return port;
+ if (!cards[(l4port >> 2)]) return port;
+
+ memset(info, 0, sizeof(struct js_l4_info));
+ info->port = l4port;
+
+ if (cards[l4port >> 2] > 0x28) js_l4_setcal(info->port, cal);
+ if (js_l4_read(info, NULL, NULL)) return port;
+
+ for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 253) u |= 1 << i;
+
+ if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0)
+ return port;
+
+ port = js_register_port(port, info, numdev, sizeof(struct js_l4_info), js_l4_read);
+
+ for (i = 0; i < numdev; i++)
+ printk(KERN_INFO "js%d: %s on L4 port %d\n",
+ js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an),
+ js_an_name(i, &info->an), js_l4_open, js_l4_close),
+ js_an_name(i, &info->an), info->port);
+
+ info = port->info;
+
+ js_l4_calibrate(info);
+ js_l4_read(info, port->axes, port->buttons);
+ js_an_init_corr(&info->an, port->axes, port->corr, 0);
+
+ return port;
+}
+
+/*
+ * js_l4_card_probe() probes for presence of the L4 card(s).
+ */
+
+static void __init js_l4_card_probe(unsigned char *cards)
+{
+ int i;
+ unsigned char rev = 0;
+
+ if (check_region(JS_L4_PORT, 1)) return;
+
+ for (i = 0; i < 2; i++) {
+
+ outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+ outb(JS_L4_SELECT_DIGITAL + i, JS_L4_PORT); /* Select card 0-1 */
+
+ if (inb(JS_L4_PORT) & JS_L4_BUSY) continue;
+ outb(JS_L4_CMD_ID, JS_L4_PORT); /* Get card ID & rev */
+
+ if (js_l4_wait_ready()) continue;
+ if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + i) continue;
+
+ if (js_l4_wait_ready()) continue;
+ if (inb(JS_L4_PORT) != JS_L4_ID) continue;
+
+ if (js_l4_wait_ready()) continue;
+ rev = inb(JS_L4_PORT);
+
+ cards[i] = rev;
+
+ printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d found at %#x\n",
+ i ? "secondary" : "primary", (i << 2), (i << 2) + 3, rev >> 4, rev & 0xf, JS_L4_PORT);
+ }
+
+}
+
+#ifndef MODULE
+void __init js_l4_setup(char *str, int *ints)
+{
+ int i;
+ for (i = 0; i <= ints[0] && i < 24; i++) js_l4[i] = ints[i+1];
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_l4_init(void)
+#endif
+{
+ int i;
+ unsigned char cards[2] = {0, 0};
+
+ js_l4_card_probe(cards);
+
+ if (js_l4[0] >= 0) {
+ for (i = 0; (js_l4[i*3] >= 0) && i < 8; i++)
+ js_l4_port = js_l4_probe(cards, js_l4[i*3], js_l4[i*3+1], js_l4[i*3+2], js_l4_port);
+ } else {
+ for (i = 0; i < 8; i++)
+ js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port);
+ }
+
+ if (js_l4_port == NULL) {
+#ifdef MODULE
+ printk(KERN_WARNING "joy-lightning: no joysticks found\n");
+#endif
+ return -ENODEV;
+ }
+
+ request_region(JS_L4_PORT, 1, "joystick (lightning)");
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ int i;
+ int cal[4] = {59, 59, 59, 59};
+ struct js_l4_info *info;
+
+ while (js_l4_port != NULL) {
+ for (i = 0; i < js_l4_port->ndevs; i++)
+ if (js_l4_port->devs[i] != NULL)
+ js_unregister_device(js_l4_port->devs[i]);
+ info = js_l4_port->info;
+ js_l4_setcal(info->port, cal);
+ js_l4_port = js_unregister_port(js_l4_port);
+ }
+ outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+ release_region(JS_L4_PORT, 1);
+}
+#endif
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
diff --git a/drivers/char/joystick/joy-sidewinder.c b/drivers/char/joystick/joy-sidewinder.c
new file mode 100644
index 000000000..3fb0d8cef
--- /dev/null
+++ b/drivers/char/joystick/joy-sidewinder.c
@@ -0,0 +1,476 @@
+/*
+ * 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.
+ */
+
+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[] = {140, 140+726, 140+300, 0};
+ int i = 0;
+ unsigned long flags;
+
+ __save_flags(flags);
+ __cli();
+ do {
+ outb(0xff,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);
+
+ for (i = 0; i < 4; i++) {
+ udelay(300);
+ outb(0xff, io);
+ }
+
+ 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(0xff,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;
+
+ if (i == l1) {
+ t = i > 64 ? 64 : i;
+ for (i = 0; i < t; i++)
+ *data |= (__u64) (buf[i] & 1) << i;
+ return t;
+ }
+ if (i == l2) {
+ t = i > 22 ? 22 : i;
+ for (i = 0; i < t; i++)
+ *data |= (__u64) buf[i] << (3 * i);
+ return t * 3;
+ }
+
+ return i;
+}
+
+/*
+ * 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 == 198) 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(0xff,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(0xff,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 != NULL) {
+ for (i = 0; i < js_sw_port->ndevs; i++)
+ if (js_sw_port->devs[i] != NULL)
+ 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
diff --git a/drivers/char/joystick/joy-thrustmaster.c b/drivers/char/joystick/joy-thrustmaster.c
new file mode 100644
index 000000000..3ea71416d
--- /dev/null
+++ b/drivers/char/joystick/joy-thrustmaster.c
@@ -0,0 +1,331 @@
+/*
+ * joy-thrustmaster.c Version 1.2
+ *
+ * Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * ThrustMaster DirectConnect (BSP) 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_TM_MAX_START 400
+#define JS_TM_MAX_STROBE 25
+#define JS_TM_MAX_LENGTH 13
+
+#define JS_TM_MODE_M3DI 1
+#define JS_TM_MODE_3DRP 3
+#define JS_TM_MODE_WCS3 4
+
+#define JS_TM_MODE_MAX 5 /* Last mode + 1 */
+
+#define JS_TM_BYTE_A0 0
+#define JS_TM_BYTE_A1 1
+#define JS_TM_BYTE_A2 3
+#define JS_TM_BYTE_A3 4
+#define JS_TM_BYTE_A4 6
+#define JS_TM_BYTE_A5 7
+
+#define JS_TM_BYTE_D0 2
+#define JS_TM_BYTE_D1 5
+#define JS_TM_BYTE_D2 8
+#define JS_TM_BYTE_D3 9
+
+#define JS_TM_BYTE_ID 10
+#define JS_TM_BYTE_REV 11
+#define JS_TM_BYTE_DEF 12
+
+static int js_tm_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_tm_port __initdata = NULL;
+
+struct js_tm_info {
+ int io;
+ unsigned char mode;
+};
+
+static int js_tm_id_to_def[JS_TM_MODE_MAX] = {0x00, 0x42, 0x00, 0x22, 0x00};
+
+/*
+ * js_tm_read_packet() reads a ThrustMaster packet.
+ */
+
+static int js_tm_read_packet(int io, unsigned char *data)
+{
+ unsigned int t, t1;
+ unsigned char u, v, error;
+ int i, j;
+ unsigned long flags;
+
+ int start = (js_time_speed * JS_TM_MAX_START) >> 10;
+ int strobe = (js_time_speed * JS_TM_MAX_STROBE) >> 10;
+
+ error = 0;
+ i = j = 0;
+
+ __save_flags(flags);
+ __cli();
+ outb(0xff,io);
+
+ t = js_get_time();
+
+ do {
+ u = inb(io);
+ t1 = js_get_time();
+ } while ((u & 1) && js_delta(t1, t) < start);
+
+ t = t1;
+ u >>= 4;
+
+ do {
+ v = inb(io) >> 4;
+ t1 = js_get_time();
+ if ((u ^ v) & u & 2) {
+ if (j) {
+ if (j < 9) { /* Data bit */
+ data[i] |= (~v & 1) << (j - 1);
+ j++;
+ } else { /* Stop bit */
+ error |= v & 1;
+ j = 0;
+ i++;
+ }
+ } else { /* Start bit */
+ data[i] = 0;
+ error |= ~v & 1;
+ j++;
+ }
+ t = t1;
+ }
+ u = v;
+ } while (!error && i < JS_TM_MAX_LENGTH && js_delta(t1,t) < strobe);
+
+ __restore_flags(flags);
+
+ return -(i != JS_TM_MAX_LENGTH);
+}
+
+/*
+ * js_tm_read() reads and analyzes ThrustMaster joystick data.
+ */
+
+static int js_tm_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_tm_info *info = xinfo;
+ unsigned char data[JS_TM_MAX_LENGTH];
+
+ if (js_tm_read_packet(info->io, data)) {
+ printk(KERN_WARNING "joy-thrustmaster: failed to read data packet\n");
+ return -1;
+ }
+ if (data[JS_TM_BYTE_ID] != info->mode) {
+ printk(KERN_WARNING "joy-thrustmaster: ID (%d) != mode (%d)\n",
+ data[JS_TM_BYTE_ID], info->mode);
+ return -1;
+ }
+ if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info->mode]) {
+ printk(KERN_WARNING "joy-thrustmaster: DEF (%d) != def(mode) (%d)\n",
+ data[JS_TM_BYTE_DEF], js_tm_id_to_def[info->mode]);
+ return -1;
+ }
+
+ switch (info->mode) {
+
+ case JS_TM_MODE_M3DI:
+
+ axes[0][0] = data[JS_TM_BYTE_A0];
+ axes[0][1] = data[JS_TM_BYTE_A1];
+ axes[0][2] = data[JS_TM_BYTE_A2];
+ axes[0][3] = data[JS_TM_BYTE_A3];
+
+ axes[0][4] = ((data[JS_TM_BYTE_D0] >> 3) & 1) - ((data[JS_TM_BYTE_D0] >> 1) & 1);
+ axes[0][5] = ((data[JS_TM_BYTE_D0] >> 2) & 1) - ( data[JS_TM_BYTE_D0] & 1);
+
+ buttons[0][0] = ((data[JS_TM_BYTE_D0] >> 6) & 0x01) | ((data[JS_TM_BYTE_D0] >> 3) & 0x06)
+ | ((data[JS_TM_BYTE_D0] >> 4) & 0x08) | ((data[JS_TM_BYTE_D1] >> 2) & 0x30);
+
+ return 0;
+
+ case JS_TM_MODE_3DRP:
+
+ axes[0][0] = data[JS_TM_BYTE_A0];
+ axes[0][1] = data[JS_TM_BYTE_A1];
+
+ buttons[0][0] = ( data[JS_TM_BYTE_D0] & 0x3f) | ((data[JS_TM_BYTE_D1] << 6) & 0xc0)
+ | (( ((int) data[JS_TM_BYTE_D0]) << 2) & 0x300);
+
+ return 0;
+
+ }
+
+ return -1;
+}
+
+/*
+ * js_tm_open() is a callback from the file open routine.
+ */
+
+static int js_tm_open(struct js_dev *jd)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_tm_close() is a callback from the file release routine.
+ */
+
+static int js_tm_close(struct js_dev *jd)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_tm_init_corr() initializes the correction values for
+ * ThrustMaster joysticks.
+ */
+
+static void __init js_tm_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 = 0;
+ corr[0][j].coef[0] = 127 - 2;
+ corr[0][j].coef[1] = 128 + 2;
+ corr[0][j].coef[2] = (1 << 29) / (127 - 4);
+ corr[0][j].coef[3] = (1 << 29) / (127 - 4);
+ }
+
+ switch (mode) {
+ case JS_TM_MODE_M3DI: j = 4; break;
+ case JS_TM_MODE_3DRP: j = 2; 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_tm_probe() probes for ThrustMaster type joysticks.
+ */
+
+static struct js_port __init *js_tm_probe(int io, struct js_port *port)
+{
+ struct js_tm_info info;
+ char *names[JS_TM_MODE_MAX] = { NULL, "ThrustMaster Millenium 3D Inceptor", NULL,
+ "ThrustMaster Rage 3D Gamepad", "ThrustMaster WCS III" };
+ char axes[JS_TM_MODE_MAX] = { 0, 6, 0, 2, 0 };
+ char buttons[JS_TM_MODE_MAX] = { 0, 5, 0, 10, 0 };
+
+ unsigned char data[JS_TM_MAX_LENGTH];
+ 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(js_tm_read_packet(io, data)) {
+ printk(KERN_WARNING "joy-thrustmaster: probe - can't read packet\n");
+ return port;
+ }
+
+ info.io = io;
+ info.mode = data[JS_TM_BYTE_ID];
+
+ if (!info.mode) return port;
+
+ if (info.mode >= JS_TM_MODE_MAX || !names[info.mode]) {
+ printk(KERN_WARNING "joy-thrustmaster: unknown device detected "
+ "(io=%#x, id=%d), contact <vojtech@ucw.cz>\n",
+ io, info.mode);
+ return port;
+ }
+
+ if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info.mode]) {
+ printk(KERN_WARNING "joy-thrustmaster: wrong DEF (%d) for ID %d - should be %d\n",
+ data[JS_TM_BYTE_DEF], info.mode, js_tm_id_to_def[info.mode]);
+ }
+
+ request_region(io, 1, "joystick (thrustmaster)");
+ port = js_register_port(port, &info, 1, sizeof(struct js_tm_info), js_tm_read);
+ printk(KERN_INFO "js%d: %s revision %d at %#x\n",
+ js_register_device(port, 0, axes[info.mode], buttons[info.mode],
+ names[info.mode], js_tm_open, js_tm_close), names[info.mode], data[JS_TM_BYTE_REV], io);
+ js_tm_init_corr(axes[info.mode], info.mode, port->axes, port->corr);
+
+ return port;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_tm_init(void)
+#endif
+{
+ int *p;
+
+ for (p = js_tm_port_list; *p; p++) js_tm_port = js_tm_probe(*p, js_tm_port);
+ if (js_tm_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-thrustmaster: no joysticks found\n");
+#endif
+
+ return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ struct js_tm_info *info;
+
+ while (js_tm_port != NULL) {
+ js_unregister_device(js_tm_port->devs[0]);
+ info = js_tm_port->info;
+ release_region(info->io, 1);
+ js_tm_port = js_unregister_port(js_tm_port);
+ }
+}
+#endif
diff --git a/drivers/char/joystick/joy-turbografx.c b/drivers/char/joystick/joy-turbografx.c
new file mode 100644
index 000000000..fdf1aa433
--- /dev/null
+++ b/drivers/char/joystick/joy-turbografx.c
@@ -0,0 +1,281 @@
+/*
+ * joy-turbografx.c Version 1.2
+ *
+ * Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Steffen Schwenke's <schwenke@burg-halle.de> TurboGraFX parallel port
+ * interface.
+ */
+
+/*
+ * 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/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_tg, "2-8i");
+MODULE_PARM(js_tg_2, "2-8i");
+MODULE_PARM(js_tg_3, "2-8i");
+
+#define JS_TG_BUTTON1 0x08
+#define JS_TG_UP 0x10
+#define JS_TG_DOWN 0x20
+#define JS_TG_LEFT 0x40
+#define JS_TG_RIGHT 0x80
+
+#define JS_TG_BUTTON2 0x02
+#define JS_TG_BUTTON3 0x04
+#define JS_TG_BUTTON4 0x01
+#define JS_TG_BUTTON5 0x08
+
+static struct js_port* js_tg_port = NULL;
+
+static int js_tg[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+static int js_tg_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+static int js_tg_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+
+struct js_tg_info {
+#ifdef USE_PARPORT
+ struct pardevice *port; /* parport device */
+#else
+ int port; /* hw port */
+#endif
+ int sticks; /* joysticks connected */
+};
+
+/*
+ * js_tg_read() reads and analyzes tg joystick data.
+ */
+
+static int js_tg_read(void *xinfo, int **axes, int **buttons)
+{
+ struct js_tg_info *info = xinfo;
+ int data1, data2, i;
+
+ for (i = 0; i < 7; i++)
+ if ((info->sticks >> i) & 1) {
+
+ JS_PAR_DATA_OUT(~(1 << i), info->port);
+ data1 = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT;
+ data2 = JS_PAR_CTRL_IN(info->port) ^ JS_PAR_CTRL_INVERT;
+
+ axes[i][0] = ((data1 & JS_TG_RIGHT) ? 1 : 0) - ((data1 & JS_TG_LEFT) ? 1 : 0);
+ axes[i][1] = ((data1 & JS_TG_DOWN ) ? 1 : 0) - ((data1 & JS_TG_UP ) ? 1 : 0);
+
+ buttons[i][0] = ((data1 & JS_TG_BUTTON1) ? 0x01 : 0) | ((data2 & JS_TG_BUTTON2) ? 0x02 : 0)
+ | ((data2 & JS_TG_BUTTON3) ? 0x04 : 0) | ((data2 & JS_TG_BUTTON4) ? 0x08 : 0)
+ | ((data2 & JS_TG_BUTTON5) ? 0x10 : 0);
+
+ }
+
+ return 0;
+}
+
+/*
+ * open callback: claim parport.
+ */
+
+int js_tg_open(struct js_dev *dev)
+{
+ struct js_tg_info *info = dev->port->info;
+
+ if (!MOD_IN_USE) {
+#ifdef USE_PARPORT
+ if (parport_claim(info->port)) return -EBUSY;
+#endif
+ JS_PAR_CTRL_OUT(0x04, info->port);
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * close callback: release parport
+ */
+
+int js_tg_close(struct js_dev *dev)
+{
+ struct js_tg_info *info = dev->port->info;
+
+ MOD_DEC_USE_COUNT;
+ if (!MOD_IN_USE) {
+ JS_PAR_CTRL_OUT(0x00, info->port);
+#ifdef USE_PARPORT
+ parport_release(info->port);
+#endif
+ }
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ struct js_tg_info *info;
+ int i;
+
+ while (js_tg_port != NULL) {
+ for (i = 0; i < js_tg_port->ndevs; i++)
+ if (js_tg_port->devs[i] != NULL)
+ js_unregister_device(js_tg_port->devs[i]);
+ info = js_tg_port->info;
+#ifdef USE_PARPORT
+ parport_unregister_device(info->port);
+#else
+ release_region(info->port, 3);
+#endif
+ js_tg_port = js_unregister_port(js_tg_port);
+ }
+}
+#endif
+
+/*
+ * js_tg_init_corr() initializes correction values of
+ * tg gamepads.
+ */
+
+static void __init js_tg_init_corr(int sticks, struct js_corr **corr)
+{
+ int i, j;
+
+ for (i = 0; i < 7; i++)
+ if ((sticks >> i) & 1)
+ for (j = 0; j < 2; 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_tg_probe() probes for tg gamepads.
+ */
+
+static struct js_port __init *js_tg_probe(int *config, struct js_port *port)
+{
+ struct js_tg_info iniinfo;
+ struct js_tg_info *info = &iniinfo;
+ int i;
+
+ if (config[0] < 0) return port;
+
+#ifdef USE_PARPORT
+ {
+ struct parport *pp;
+
+ if (config[0] > 0x10)
+ for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next);
+ else
+ for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--;
+
+ if (pp == NULL) {
+ printk(KERN_ERR "joy-tg: no such parport\n");
+ return port;
+ }
+
+ info->port = parport_register_device(pp, "joystick (turbografx)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+ if (!info->port)
+ return port;
+ }
+#else
+ info->port = config[0];
+ if (check_region(info->port, 3)) return port;
+ request_region(info->port, 3, "joystick (turbografx)");
+#endif
+
+ port = js_register_port(port, info, 7, sizeof(struct js_tg_info), js_tg_read);
+ info = port->info;
+
+ info->sticks = 0;
+
+ for (i = 0; i < 7; i++)
+ if (config[i+1] > 0 && config[i+1] < 6) {
+#ifdef USE_PARPORT
+ printk(KERN_INFO "js%d: Multisystem joystick on %s\n",
+ js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close),
+ info->port->port->name);
+#else
+ printk(KERN_INFO "js%d: Multisystem joystick at %#x\n",
+ js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close),
+ info->port);
+#endif
+ info->sticks |= (1 << i);
+ }
+
+ if (!info->sticks) {
+#ifdef USE_PARPORT
+ parport_unregister_device(info->port);
+#else
+ release_region(info->port, 3);
+#endif
+ return port;
+ }
+
+ js_tg_init_corr(info->sticks, port->corr);
+
+ return port;
+}
+
+#ifndef MODULE
+void __init js_tg_setup(char *str, int *ints)
+{
+ int i;
+
+ if (!strcmp(str,"js_tg"))
+ for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1];
+ if (!strcmp(str,"js_tg_2"))
+ for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1];
+ if (!strcmp(str,"js_tg_3"))
+ for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1];
+
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_tg_init(void)
+#endif
+{
+ js_tg_port = js_tg_probe(js_tg, js_tg_port);
+ js_tg_port = js_tg_probe(js_tg_2, js_tg_port);
+ js_tg_port = js_tg_probe(js_tg_3, js_tg_port);
+
+ if (js_tg_port) return 0;
+
+#ifdef MODULE
+ printk(KERN_WARNING "joy-tg: no joysticks specified\n");
+#endif
+ return -ENODEV;
+}
diff --git a/drivers/char/joystick/joystick.c b/drivers/char/joystick/joystick.c
new file mode 100644
index 000000000..aff1acd17
--- /dev/null
+++ b/drivers/char/joystick/joystick.c
@@ -0,0 +1,1231 @@
+/*
+ * joystick.c Version 1.2
+ *
+ * Copyright (c) 1996-1998 Vojtech Pavlik
+ */
+
+/*
+ * This is the main joystick driver for Linux. It doesn't support any
+ * devices directly, rather is lets you use sub-modules to do that job. See
+ * Documentation/joystick.txt for more info.
+ */
+
+/*
+ * 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 <asm/segment.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <asm/spinlock.h>
+#include <linux/poll.h>
+#endif
+
+/*
+ * Configurable parameters.
+ */
+
+#define JS_REFRESH_TIME HZ/50 /* Time between two reads of joysticks (20ms) */
+
+/*
+ * Buffer macros.
+ */
+
+#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C)))))
+#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1)
+#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1)
+#define DIFF(X,Y) ((X)>(Y)?(X)-(Y):(Y)-(X))
+
+/*
+ * Global variables.
+ */
+
+static struct JS_DATA_SAVE_TYPE js_comp_glue;
+static struct js_port *js_port = NULL;
+static struct js_dev *js_dev = NULL;
+static struct timer_list js_timer;
+spinlock_t js_lock = SPIN_LOCK_UNLOCKED;
+static int js_use_count = 0;
+
+/*
+ * Exported variables.
+ */
+
+unsigned int js_time_speed = 0;
+js_time_func js_get_time;
+js_delta_func js_delta;
+
+unsigned int js_time_speed_a = 0;
+js_time_func js_get_time_a;
+js_delta_func js_delta_a;
+
+/*
+ * Module info.
+ */
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_SUPPORTED_DEVICE("js");
+
+/*
+ * js_get_time_*() are different functions to get current time.
+ * js_delta_*() are functions to compute time difference.
+ */
+
+#ifdef __i386__
+
+static unsigned int js_get_time_rdtsc(void)
+{
+ unsigned int x;
+ __asm__ __volatile__ ( "rdtsc" : "=A" (x) );
+ return x;
+}
+
+static unsigned int js_get_time_pit(void)
+{
+ unsigned long flags;
+ unsigned int x;
+
+ __save_flags(flags);
+ __cli();
+ outb(0, 0x43);
+ x = inb(0x40);
+ x |= inb(0x40) << 8;
+ __restore_flags(flags);
+
+ return x;
+}
+
+static int js_delta_pit(unsigned int x, unsigned int y)
+{
+ return y - x + ( y < x ? 1193180L / HZ : 0 );
+}
+
+static unsigned int js_get_time_counter(void)
+{
+ static int time_counter = 0;
+ return time_counter++;
+}
+
+#else
+#ifdef __alpha__
+
+static unsigned int js_get_time_rpcc(void)
+{
+ unsigned int x;
+ __asm__ __volatile__ ( "rpcc %0" : "=r" (x) );
+ return x;
+}
+
+#else
+
+#ifndef MODULE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+static unsigned int js_get_time_system(void)
+{
+ static struct timeval js_tv;
+ get_fast_time(&js_tv);
+ return js_tv.tv_sec * 1000000L + js_tv.tv_usec;
+}
+#endif
+#endif
+
+#endif
+#endif
+
+static int js_delta_normal(unsigned int x, unsigned int y)
+{
+ return x - y;
+}
+
+/*
+ * js_calibrate_time() calibrates a given timer.
+ */
+
+static int __init js_calibrate_time(js_time_func get_time, js_delta_func delta)
+{
+ unsigned int t1, t2, t3;
+ unsigned long flags;
+
+ __save_flags(flags);
+ __cli();
+ t1 = get_time();
+ udelay(1000);
+ t2 = get_time();
+ t3 = get_time();
+ __restore_flags(flags);
+
+ return delta(t2, t1) - delta(t3, t2);
+}
+
+/*
+ * js_calibrate_time_counter() calibrates the counter timer, which can't
+ * be calibrated using the above function.
+ */
+
+#ifdef __i386__
+
+static int __init js_calibrate_time_counter(void)
+{
+ unsigned int i, j, t1, t2, t3;
+
+ j = jiffies; do { inb(0x201); t1 = js_get_time_counter(); } while (j == jiffies);
+ j = jiffies; do { inb(0x201); t2 = js_get_time_counter(); } while (j == jiffies);
+
+ j = (t2 - t1) * HZ / 1000;
+
+ t1 = js_get_time_pit();
+ for (i = 0; i < 1000; i++) {
+ inb(0x201);
+ js_get_time_counter();
+ }
+ t2 = js_get_time_pit();
+ t3 = js_get_time_pit();
+
+ i = 1193180L / (js_delta_pit(t2, t1) - js_delta_pit(t3, t2));
+
+ if (DIFF(i,j) > 5)
+ printk(KERN_WARNING "js: Counter timer calibration unsure,"
+ " pass1 (0.%d MHz) and pass2 (0.%d MHz) differ.\n", j, i);
+
+ return (i + j) >> 1;
+}
+
+#endif
+
+/*
+ * js_setup_time chooses the best available timers
+ * on the system and calibrates them.
+ */
+
+static int __init js_setup_time(void)
+{
+ int t;
+ char *name, *name_a;
+
+ name = "";
+ name_a = "";
+ js_time_speed = 0;
+ js_time_speed_a = 0;
+
+#ifdef __i386__
+
+ t = js_calibrate_time(js_get_time_pit, js_delta_pit);
+
+ if (DIFF(t, 1193) > 5)
+ printk(KERN_WARNING "js: Measured PIT speed is %d.%03d MHz, but should be 1.193 MHz.\n"
+ KERN_WARNING "js: This is probably caused by wrong BogoMIPS value. It is: %ld, should be: %ld.\n",
+ t / 1000, t % 1000, loops_per_sec / 500000, loops_per_sec / (t * 500000 / 1193));
+
+ if (JS_HAS_RDTSC && (t = js_calibrate_time(js_get_time_rdtsc, js_delta_normal)) > 0) {
+
+ js_time_speed_a = t;
+ js_get_time_a = js_get_time_rdtsc;
+ js_delta_a = js_delta_normal;
+ js_time_speed = t;
+ js_get_time = js_get_time_rdtsc;
+ js_delta = js_delta_normal;
+ name = "RDTSC";
+
+ } else {
+
+ js_time_speed_a = t;
+ js_get_time_a = js_get_time_pit;
+ js_delta_a = js_delta_pit;
+ name_a = "PIT";
+
+ t = js_calibrate_time_counter();
+
+ js_time_speed = t;
+ js_get_time = js_get_time_counter;
+ js_delta = js_delta_normal;
+ name = "counter";
+
+ }
+
+#else
+#ifdef __alpha__
+
+ t = js_calibrate_time(js_get_time_rpcc, js_delta_normal);
+
+ js_time_speed_a = t;
+ js_get_time_a = js_get_time_rpcc;
+ js_delta_a = js_delta_normal;
+ js_time_speed = t;
+ js_get_time = js_get_time_rpcc;
+ js_delta = js_delta_normal;
+ name = "RPCC";
+
+#else
+
+#ifndef MODULE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ t = js_calibrate_time(js_get_time_system, js_delta_normal);
+
+ js_time_speed_a = t;
+ js_get_time_a = js_get_time_system;
+ js_delta_a = js_delta_normal;
+ js_time_speed = t;
+ js_get_time = js_get_time_system;
+ js_delta = js_delta_normal;
+ name = "system";
+#endif
+#endif
+
+#endif
+#endif
+
+ printk(KERN_INFO "js: Version %d.%d.%d ",
+ JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff);
+
+ if (js_time_speed_a <= 0 || js_time_speed <= 0) {
+ printk("\n");
+ return -1;
+ }
+
+ printk("using ");
+
+ if (js_time_speed > 10000) {
+ t = js_time_speed / 1000 + (js_time_speed % 1000 >= 500);
+ printk("%d MHz ", t);
+ } else {
+ t = js_time_speed / 10 + (js_time_speed % 10 >= 5);
+ printk("%d.%02d MHz ", t / 100, t % 100);
+ }
+
+ if (js_get_time_a != js_get_time) {
+ t = js_time_speed_a / 10 + (js_time_speed_a % 10 >= 5);
+ printk("%s timer and %d.%02d MHz %s timer.\n",
+ name, t / 100, t % 100, name_a);
+ } else {
+ printk("%s timer.\n", name);
+ }
+
+ return 0;
+}
+
+
+/*
+ * js_correct() performs correction of raw joystick data.
+ */
+
+static int js_correct(int value, struct js_corr *corr)
+{
+ switch (corr->type) {
+ case JS_CORR_NONE:
+ break;
+ case JS_CORR_BROKEN:
+ value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
+ ((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
+ ((corr->coef[2] * (value - corr->coef[0])) >> 14);
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (value < -32767) return -32767;
+ if (value > 32767) return 32767;
+
+ return value;
+}
+
+/*
+ * js_button() returns value of button number i.
+ */
+
+static inline int js_button(int *buttons, int i)
+{
+ return (buttons[i >> 5] >> (i & 0x1f)) & 1;
+}
+
+
+/*
+ * js_add_event() adds an event to the buffer. This requires additional
+ * queue post-processing done by js_sync_buff.
+ */
+
+static void js_add_event(struct js_dev *jd, __u32 time, __u8 type, __u8 number, __s16 value)
+{
+ jd->buff[jd->ahead].time = time;
+ jd->buff[jd->ahead].type = type;
+ jd->buff[jd->ahead].number = number;
+ jd->buff[jd->ahead].value = value;
+ if (++jd->ahead == JS_BUFF_SIZE) jd->ahead = 0;
+}
+
+/*
+ * js_flush_data() does the same as js_process_data, except for that it doesn't
+ * generate any events - it just copies the data from new to cur.
+ */
+
+static void js_flush_data(struct js_dev *jd)
+{
+ int i;
+
+ for (i = 0; i < ((jd->num_buttons - 1) >> 5) + 1; i++)
+ jd->cur.buttons[i] = jd->new.buttons[i];
+ for (i = 0; i < jd->num_axes; i++)
+ jd->cur.axes[i] = jd->new.axes[i];
+}
+
+/*
+ * js_process_data() finds changes in button states and axis positions and adds
+ * them as events to the buffer.
+ */
+
+static void js_process_data(struct js_dev *jd)
+{
+ int i, t;
+
+ for (i = 0; i < jd->num_buttons; i++)
+ if ((t = js_button(jd->new.buttons, i)) != js_button(jd->cur.buttons, i)) {
+ js_add_event(jd, jiffies, JS_EVENT_BUTTON, i, t);
+ jd->cur.buttons[i >> 5] ^= (1 << (i & 0x1f));
+ }
+
+ for (i = 0; i < jd->num_axes; i++) {
+ t = js_correct(jd->new.axes[i], &jd->corr[i]);
+ if (((jd->corr[i].prec == -1) && t) ||
+ ((DIFF(jd->new.axes[i], jd->cur.axes[i]) > jd->corr[i].prec) &&
+ (t != js_correct(jd->cur.axes[i], &jd->corr[i])))) {
+ js_add_event(jd, jiffies, JS_EVENT_AXIS, i, t);
+ jd->cur.axes[i] = jd->new.axes[i];
+ }
+ }
+}
+
+/*
+ * js_sync_buff() checks for all overflows caused by recent additions to the buffer.
+ * These happen only if some process is reading the data too slowly. It
+ * wakes up any process waiting for data.
+ */
+
+static void js_sync_buff(struct js_dev *jd)
+{
+ struct js_list *curl = jd->list;
+
+ if (jd->bhead != jd->ahead) {
+ if(ROT(jd->bhead, jd->tail, jd->ahead) || (jd->tail == jd->bhead)) {
+ while (curl) {
+ if (ROT(jd->bhead, curl->tail, jd->ahead) || (curl->tail == jd->bhead)) {
+ curl->tail = jd->ahead;
+ curl->startup = 0;
+ }
+ curl = curl->next;
+ }
+ jd->tail = jd->ahead;
+ }
+ jd->bhead = jd->ahead;
+ wake_up_interruptible(&jd->wait);
+ }
+}
+
+/*
+ * js_do_timer() acts as an interrupt replacement. It reads the data
+ * from all ports and then generates events for all devices.
+ */
+
+static void js_do_timer(unsigned long data)
+{
+ struct js_port *curp = js_port;
+ struct js_dev *curd = js_dev;
+ unsigned long flags;
+
+ while (curp != NULL) {
+ curp->read(curp->info, curp->axes, curp->buttons);
+ curp = curp->next;
+ }
+
+ spin_lock_irqsave(&js_lock, flags);
+
+ while (curd != NULL) {
+ if (data) {
+ js_process_data(curd);
+ js_sync_buff(curd);
+ } else {
+ js_flush_data(curd);
+ }
+ curd = curd->next;
+ }
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ js_timer.expires = jiffies + JS_REFRESH_TIME;
+ add_timer(&js_timer);
+}
+
+/*
+ * js_read() copies one or more entries from jsd[].buff to user
+ * space.
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+#else
+static int js_read(struct inode *inode, struct file *file, char *buf, int count)
+#endif
+{
+ struct wait_queue wait = { current, NULL };
+ struct js_event *buff = (void *) buf;
+ struct js_list *curl;
+ struct js_dev *jd;
+ unsigned long blocks = count / sizeof(struct js_event);
+ int written = 0;
+ int new_tail, orig_tail;
+ int retval = 0;
+ unsigned long flags;
+
+ curl = file->private_data;
+ jd = curl->dev;
+ orig_tail = curl->tail;
+
+/*
+ * Check user data.
+ */
+
+ if (!blocks)
+ return -EINVAL;
+
+/*
+ * Lock it.
+ */
+
+ spin_lock_irqsave(&js_lock, flags);
+
+/*
+ * Handle (non)blocking i/o.
+ */
+ if (count != sizeof(struct JS_DATA_TYPE)) {
+
+ if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) {
+
+ add_wait_queue(&jd->wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ while (GOF(curl->tail) == jd->bhead) {
+
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+
+ spin_unlock_irqrestore(&js_lock, flags);
+ schedule();
+ spin_lock_irqsave(&js_lock, flags);
+
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&jd->wait, &wait);
+ }
+
+ if (retval) {
+ spin_unlock_irqrestore(&js_lock, flags);
+ return retval;
+ }
+
+/*
+ * Initial state.
+ */
+
+ while (curl->startup < jd->num_axes + jd->num_buttons && written < blocks && !retval) {
+
+ struct js_event tmpevent;
+
+ if (curl->startup < jd->num_buttons) {
+ tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+ tmpevent.value = js_button(jd->cur.buttons, curl->startup);
+ tmpevent.number = curl->startup;
+ } else {
+ tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+ tmpevent.value = js_correct(jd->cur.axes[curl->startup - jd->num_buttons],
+ &jd->corr[curl->startup - jd->num_buttons]);
+ tmpevent.number = curl->startup - jd->num_buttons;
+ }
+
+ tmpevent.time = jiffies * (1000/HZ);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event)))
+ retval = -EFAULT;
+#else
+ if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event))))
+ memcpy_tofs(&buff[written], &tmpevent, sizeof(struct js_event));
+#endif
+
+ curl->startup++;
+ written++;
+ }
+
+/*
+ * Buffer data.
+ */
+
+ while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) {
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event)))
+ retval = -EFAULT;
+ if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time))
+ retval = -EFAULT;
+#else
+ if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) {
+ memcpy_tofs(&buff[written], &jd->buff[new_tail], sizeof(struct js_event));
+ put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time);
+ }
+#endif
+ curl->tail = new_tail;
+ written++;
+ }
+ }
+
+ else
+
+/*
+ * Handle version 0.x compatibility.
+ */
+
+ {
+ struct JS_DATA_TYPE data;
+
+ data.buttons = jd->new.buttons[0];
+ data.x = jd->num_axes < 1 ? 0 :
+ ((js_correct(jd->new.axes[0], &jd->corr[0]) / 256) + 128) >> js_comp_glue.JS_CORR.x;
+ data.y = jd->num_axes < 2 ? 0 :
+ ((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+#else
+ if (!(retval = verify_area(VERIFY_WRITE, buf, sizeof(struct JS_DATA_TYPE)))) {
+ memcpy_tofs(buf, &data, sizeof(struct JS_DATA_TYPE));
+ }
+#endif
+
+ curl->startup = 0;
+ curl->tail = GOB(jd->bhead);
+ if (!retval) retval = sizeof(struct JS_DATA_TYPE);
+ }
+
+/*
+ * Check main tail and move it.
+ */
+
+ if (orig_tail == jd->tail) {
+ new_tail = curl->tail;
+ curl = jd->list;
+ while (curl != NULL && curl->tail != jd->tail) {
+ if (ROT(jd->bhead, new_tail, curl->tail) ||
+ (jd->bhead == curl->tail)) new_tail = curl->tail;
+ curl = curl->next;
+ }
+ if (curl == NULL) jd->tail = new_tail;
+ }
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ return retval ? retval : written * sizeof(struct js_event);
+}
+
+/*
+ * js_poll() does select() support.
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+
+static unsigned int js_poll(struct file *file, poll_table *wait)
+{
+ struct js_list *curl = file->private_data;
+ unsigned long flags;
+ int retval = 0;
+ poll_wait(file, &curl->dev->wait, wait);
+ spin_lock_irqsave(&js_lock, flags);
+ if (GOF(curl->tail) != curl->dev->bhead ||
+ curl->startup < curl->dev->num_axes + curl->dev->num_buttons) retval = POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&js_lock, flags);
+ return retval;
+}
+
+#else
+
+static int js_select(struct inode *inode, struct file *file, int sel_type, select_table *wait)
+{
+ struct js_list *curl = file->private_data;
+ if (sel_type == SEL_IN) {
+ if (GOF(curl->tail) != curl->dev->bhead) return 1;
+ select_wait(&curl->dev->wait, wait);
+ }
+ return 0;
+}
+
+#endif
+
+/*
+ * js_ioctl handles misc ioctl calls.
+ */
+
+static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct js_list *curl;
+ struct js_dev *jd;
+ int len;
+
+ curl = file->private_data;
+ jd = curl->dev;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+
+ switch (cmd) {
+
+/*
+ * 0.x compatibility
+ */
+
+ case JS_SET_CAL:
+ return copy_from_user(&js_comp_glue.JS_CORR, (struct JS_DATA_TYPE *) arg,
+ sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+ case JS_GET_CAL:
+ return copy_to_user((struct JS_DATA_TYPE *) arg, &js_comp_glue.JS_CORR,
+ sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+ case JS_SET_TIMEOUT:
+ return get_user(js_comp_glue.JS_TIMEOUT, (int *) arg);
+ case JS_GET_TIMEOUT:
+ return put_user(js_comp_glue.JS_TIMEOUT, (int *) arg);
+ case JS_SET_TIMELIMIT:
+ return get_user(js_comp_glue.JS_TIMELIMIT, (long *) arg);
+ case JS_GET_TIMELIMIT:
+ return put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg);
+ case JS_SET_ALL:
+ return copy_from_user(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg,
+ sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+ case JS_GET_ALL:
+ return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue,
+ sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+
+/*
+ * 1.x ioctl calls
+ */
+
+ case JSIOCGVERSION:
+ return put_user(JS_VERSION, (__u32 *) arg);
+ case JSIOCGAXES:
+ return put_user(jd->num_axes, (__u8 *) arg);
+ case JSIOCGBUTTONS:
+ return put_user(jd->num_buttons, (__u8 *) arg);
+ case JSIOCSCORR:
+ return copy_from_user(jd->corr, (struct js_corr *) arg,
+ sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0;
+ case JSIOCGCORR:
+ return copy_to_user((struct js_corr *) arg, jd->corr,
+ sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0;
+ default:
+ if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+ len = strlen(jd->name) + 1;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ if (copy_to_user((char *) arg, jd->name, len)) return -EFAULT;
+ return len;
+ }
+ }
+
+#else
+
+ switch (cmd) {
+
+/*
+ * 0.x compatibility
+ */
+
+ case JS_SET_CAL:
+ if (verify_area(VERIFY_READ, (struct JS_DATA_TYPE *) arg,
+ sizeof(struct JS_DATA_TYPE))) return -EFAULT;
+ memcpy_fromfs(&js_comp_glue.JS_CORR, (struct JS_DATA_SAVE_TYPE *) arg,
+ sizeof(struct JS_DATA_TYPE));
+ return 0;
+ case JS_GET_CAL:
+ if (verify_area(VERIFY_WRITE, (struct JS_DATA_TYPE *) arg,
+ sizeof(struct JS_DATA_TYPE))) return -EFAULT;
+ memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue.JS_CORR,
+ sizeof(struct JS_DATA_TYPE));
+ return 0;
+ case JS_SET_TIMEOUT:
+ if (verify_area(VERIFY_READ, (int *) arg, sizeof(int))) return -EFAULT;
+ js_comp_glue.JS_TIMEOUT = get_user((int *) arg);
+ return 0;
+ case JS_GET_TIMEOUT:
+ if (verify_area(VERIFY_WRITE, (int *) arg, sizeof(int))) return -EFAULT;
+ put_user(js_comp_glue.JS_TIMEOUT, (int *) arg);
+ return 0;
+ case JS_SET_TIMELIMIT:
+ if (verify_area(VERIFY_READ, (long *) arg, sizeof(long))) return -EFAULT;
+ js_comp_glue.JS_TIMELIMIT = get_user((long *) arg);
+ return 0;
+ case JS_GET_TIMELIMIT:
+ if (verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))) return -EFAULT;
+ put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg);
+ return 0;
+ case JS_SET_ALL:
+ if (verify_area(VERIFY_READ, (struct JS_DATA_SAVE_TYPE *) arg,
+ sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT;
+ memcpy_fromfs(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg,
+ sizeof(struct JS_DATA_SAVE_TYPE));
+ return 0;
+ case JS_GET_ALL:
+ if (verify_area(VERIFY_WRITE, (struct JS_DATA_SAVE_TYPE *) arg,
+ sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT;
+ memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue,
+ sizeof(struct JS_DATA_SAVE_TYPE));
+ return 0;
+
+/*
+ * 1.x ioctl calls
+ */
+
+ case JSIOCGVERSION:
+ if (verify_area(VERIFY_WRITE, (__u32 *) arg, sizeof(__u32))) return -EFAULT;
+ put_user(JS_VERSION, (__u32 *) arg);
+ return 0;
+ case JSIOCGAXES:
+ if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT;
+ put_user(jd->num_axes, (__u8 *) arg);
+ return 0;
+ case JSIOCGBUTTONS:
+ if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT;
+ put_user(jd->num_buttons, (__u8 *) arg);
+ return 0;
+ case JSIOCSCORR:
+ if (verify_area(VERIFY_READ, (struct js_corr *) arg,
+ sizeof(struct js_corr) * jd->num_axes)) return -EFAULT;
+ memcpy_fromfs(jd->corr, (struct js_corr *) arg,
+ sizeof(struct js_corr) * jd->num_axes);
+ return 0;
+ case JSIOCGCORR:
+ if (verify_area(VERIFY_WRITE, (struct js_corr *) arg,
+ sizeof(struct js_corr) * jd->num_axes)) return -EFAULT;
+ memcpy_tofs((struct js_corr *) arg,
+ jd->corr, sizeof(struct js_corr) * jd->num_axes);
+ return 0;
+ default:
+ if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+ len = strlen(jd->name) + 1;
+ if (verify_area(VERIFY_WRITE, (char *) arg, len)) return -EFAULT;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ memcpy_tofs((char *) arg, jd->name, len);
+ return len;
+ }
+ }
+
+#endif
+
+ return -EINVAL;
+}
+
+/*
+ * js_open() performs necessary initialization and adds
+ * an entry to the linked list.
+ */
+
+static int js_open(struct inode *inode, struct file *file)
+{
+ struct js_list *curl, *new;
+ struct js_dev *jd = js_dev;
+ int i = MINOR(inode->i_rdev);
+ unsigned long flags;
+ int result;
+
+ if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR)
+ return -EINVAL;
+
+ spin_lock_irqsave(&js_lock, flags);
+
+ while (i > 0 && jd != NULL) {
+ jd = jd->next;
+ i--;
+ }
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ if (jd == NULL) return -ENODEV;
+
+ if ((result = jd->open(jd))) return result;
+
+ MOD_INC_USE_COUNT;
+ if (!js_use_count++) js_do_timer(0);
+
+ if ((new = kmalloc(sizeof(struct js_list), GFP_KERNEL)) != NULL) {
+
+ spin_lock_irqsave(&js_lock, flags);
+
+ curl = jd->list;
+
+ jd->list = new;
+ jd->list->next = curl;
+ jd->list->dev = jd;
+ jd->list->startup = 0;
+ jd->list->tail = GOB(jd->bhead);
+ file->private_data = jd->list;
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ } else {
+ result = -ENOMEM;
+ }
+
+ return result;
+}
+
+/*
+ * js_release() removes an entry from list and deallocates memory
+ * used by it.
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+static int js_release(struct inode *inode, struct file *file)
+#else
+static void js_release(struct inode *inode, struct file *file)
+#endif
+{
+ struct js_list *curl = file->private_data;
+ struct js_dev *jd = curl->dev;
+ struct js_list **curp = &jd->list;
+ int new_tail;
+ unsigned long flags;
+
+ spin_lock_irqsave(&js_lock, flags);
+
+ while (*curp && (*curp != curl)) curp = &((*curp)->next);
+ *curp = (*curp)->next;
+
+ if (jd->list != NULL)
+ if (curl->tail == jd->tail) {
+ curl = jd->list;
+ new_tail = curl->tail;
+ while (curl != NULL && curl->tail != jd->tail) {
+ if (ROT(jd->bhead, new_tail, curl->tail) ||
+ (jd->bhead == curl->tail)) new_tail = curl->tail;
+ curl = curl->next;
+ }
+ if (!curl) jd->tail = new_tail;
+ }
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ kfree(file->private_data);
+
+ if (!--js_use_count) del_timer(&js_timer);
+ MOD_DEC_USE_COUNT;
+
+ jd->close(jd);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ return 0;
+#endif
+}
+
+/*
+ * js_dump_mem() dumps all data structures in memory.
+ * It's used for debugging only.
+ */
+
+#if 0
+static void js_dump_mem(void)
+{
+
+ struct js_port *curp = js_port;
+ struct js_dev *curd = js_dev;
+ int i;
+
+ printk(",--- Dumping Devices:\n");
+ printk("| js_dev = %x\n", (int) js_dev);
+
+ while (curd != NULL) {
+ printk("| %s-device %x, next %x axes %d, buttons %d, port %x - %#x\n",
+ curd->next ? "|":"`",
+ (int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port, curd->port->io);
+ curd = curd->next;
+ }
+
+ printk(">--- Dumping ports:\n");
+ printk("| js_port = %x\n", (int) js_port);
+
+ while (curp != NULL) {
+ printk("| %s-port %x, next %x, io %#x, devices %d\n",
+ curp->next ? "|":"`",
+ (int) curp, (int) curp->next, curp->io, curp->ndevs);
+ for (i = 0; i < curp->ndevs; i++) {
+ curd = curp->devs[i];
+ if (curd)
+ printk("| %s %s-device %x, next %x axes %d, buttons %d, port %x\n",
+ curp->next ? "|":" ", (i < curp->ndevs-1) ? "|":"`",
+ (int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port);
+ else
+ printk("| %s %s-device %x, not there\n",
+ curp->next ? "|":" ", (i < curp->ndevs-1) ? "|":"`", (int) curd);
+
+ }
+ curp = curp->next;
+ }
+
+ printk("`--- Done\n");
+}
+#endif
+
+
+struct js_port *js_register_port(struct js_port *port,
+ void *info, int devs, int infos, js_read_func read)
+{
+ struct js_port **ptrp = &js_port;
+ struct js_port *curp;
+ void *all;
+ int i;
+ unsigned long flags;
+
+ if ((all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL)) == NULL)
+ return NULL;
+
+ curp = all;
+
+ curp->next = NULL;
+ curp->prev = port;
+ curp->read = read;
+ curp->ndevs = devs;
+
+ curp->devs = all += sizeof(struct js_port);
+ for (i = 0; i < devs; i++) curp->devs[i] = NULL;
+
+ curp->axes = all += devs * sizeof(void*);
+ curp->buttons = (void*) all += devs * sizeof(void*);
+ curp->corr = all += devs * sizeof(void*);
+
+ if (infos) {
+ curp->info = all += devs * sizeof(void*);
+ memcpy(curp->info, info, infos);
+ } else {
+ curp->info = NULL;
+ }
+
+ spin_lock_irqsave(&js_lock, flags);
+
+ while (*ptrp != NULL) ptrp=&((*ptrp)->next);
+ *ptrp = curp;
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ return curp;
+}
+
+struct js_port *js_unregister_port(struct js_port *port)
+{
+ struct js_port **curp = &js_port;
+ struct js_port *prev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&js_lock, flags);
+
+ while (*curp != NULL && (*curp != port)) curp = &((*curp)->next);
+ *curp = (*curp)->next;
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ prev = port->prev;
+ kfree(port);
+
+ return prev;
+}
+
+int js_register_device(struct js_port *port, int number, int axes, int buttons, char *name,
+ js_ops_func open, js_ops_func close)
+{
+ struct js_dev **ptrd = &js_dev;
+ struct js_dev *curd;
+ void *all;
+ int i = 0;
+ unsigned long flags;
+
+ if ((all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) +
+ 2 * (((buttons - 1) >> 5) + 1) * sizeof(int) +
+ axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL)) == NULL)
+ return -1;
+
+ curd = all;
+
+ curd->next = NULL;
+ curd->list = NULL;
+ curd->port = port;
+ curd->wait = NULL;
+ curd->open = open;
+ curd->close = close;
+
+ curd->ahead = 0;
+ curd->bhead = 0;
+ curd->tail = JS_BUFF_SIZE - 1;
+ curd->num_axes = axes;
+ curd->num_buttons = buttons;
+
+ curd->cur.axes = all += sizeof(struct js_dev);
+ curd->cur.buttons = all += axes * sizeof(int);
+ curd->new.axes = all += (((buttons - 1) >> 5) + 1) * sizeof(int);
+ curd->new.buttons = all += axes * sizeof(int);
+ curd->corr = all += (((buttons -1 ) >> 5) + 1) * sizeof(int);
+
+ curd->name = all += axes * sizeof(struct js_corr);
+ strcpy(curd->name, name);
+
+ port->devs[number] = curd;
+ port->axes[number] = curd->new.axes;
+ port->buttons[number] = curd->new.buttons;
+ port->corr[number] = curd->corr;
+
+ spin_lock_irqsave(&js_lock, flags);
+
+ while (*ptrd != NULL) { ptrd=&(*ptrd)->next; i++; }
+ *ptrd = curd;
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ return i;
+}
+
+void js_unregister_device(struct js_dev *dev)
+{
+ struct js_dev **curd = &js_dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&js_lock, flags);
+
+ while (*curd != NULL && (*curd != dev)) curd = &((*curd)->next);
+ *curd = (*curd)->next;
+
+ spin_unlock_irqrestore(&js_lock, flags);
+
+ kfree(dev);
+}
+
+/*
+ * The operations structure.
+ */
+
+static struct file_operations js_fops =
+{
+ read: js_read,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ poll: js_poll,
+#else
+ select: js_select,
+#endif
+ ioctl: js_ioctl,
+ open: js_open,
+ release: js_release,
+};
+
+/*
+ * js_init() registers the driver and calls the probe function.
+ * also initializes some crucial variables.
+ */
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_init(void)
+#endif
+{
+ int result;
+
+ js_setup_time();
+
+ if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
+ printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
+ return -EBUSY;
+ }
+
+ spin_lock_init(&js_lock);
+
+ init_timer(&js_timer);
+ js_timer.function = js_do_timer;
+ js_timer.data = 1;
+
+ memset(&js_comp_glue, 0, sizeof(struct JS_DATA_SAVE_TYPE));
+ js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT;
+ js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT;
+
+#ifdef MODULE
+ result = 0;
+#else
+ result = -ENODEV;
+#ifdef CONFIG_JOY_LIGHTNING
+ if (!js_l4_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_SIDEWINDER
+ if (!js_sw_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_ASSASIN
+ if (!js_as_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_LOGITECH
+ if (!js_lt_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_THRUSTMASTER
+ if (!js_tm_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_GRAVIS
+ if (!js_gr_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_ANALOG
+ if (!js_an_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_CONSOLE
+ if (!js_console_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_DB9
+ if (!js_db9_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_AMIGA
+ if (!js_am_init()) result = 0;
+#endif
+ if (result) printk(KERN_ERR "js: no joysticks found\n");
+#endif
+
+ return result;
+}
+
+/*
+ * cleanup_module() handles module removal.
+ */
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ del_timer(&js_timer);
+ if (unregister_chrdev(JOYSTICK_MAJOR, "js"))
+ printk(KERN_ERR "js: can't unregister device\n");
+}
+#endif