11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) 1996-2001 Vojtech Pavlik
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Analog joystick and gamepad driver for Linux
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Should you need to contact me, the author, you can do so either by
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bitops.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/input.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/gameport.h>
374e57b6817880946a3a78d5d8cad1ace363f7e449Tim Schmielau#include <linux/jiffies.h>
3808604bd9935dc98fb62ef61d5b7baa7ccc10f8c2Arnd Bergmann#include <linux/timex.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC	"Analog joystick and gamepad driver"
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(DRIVER_DESC);
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Option parsing.
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_PORTS		16
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char *js[ANALOG_PORTS];
5378167236e23bb3c80d2b35b693e578a6e56b1171Dmitry Torokhovstatic unsigned int js_nargs;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int analog_options[ANALOG_PORTS];
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array_named(map, js, charp, &js_nargs, 0);
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities");
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Times, feature definitions.
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_RUDDER		0x00004
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_THROTTLE		0x00008
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_AXES_STD		0x0000f
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTNS_STD		0x000f0
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTNS_CHF		0x00100
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_HAT1_CHF		0x00200
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_HAT2_CHF		0x00400
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_HAT_FCS		0x00800
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_HATS_ALL		0x00e00
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTN_TL		0x01000
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTN_TR		0x02000
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTN_TL2		0x04000
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTN_TR2		0x08000
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTNS_TLR		0x03000
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTNS_TLR2	0x0c000
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_BTNS_GAMEPAD	0x0f000
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_HBTN_CHF		0x10000
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_ANY_CHF		0x10700
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_SAITEK		0x20000
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_EXTENSIONS	0x7ff00
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_GAMEPAD		0x80000
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_MAX_TIME		3	/* 3 ms */
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_LOOP_TIME	2000	/* 2 * loop */
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_SAITEK_DELAY	200	/* 200 us */
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_SAITEK_TIME	2000	/* 2000 us */
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_AXIS_TIME	2	/* 2 * refresh */
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_INIT_RETRIES	8	/* 8 times */
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_FUZZ_BITS	2	/* 2 bit more */
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_FUZZ_MAGIC	36	/* 36 u*ms/loop */
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_MAX_NAME_LENGTH  128
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ANALOG_MAX_PHYS_LENGTH	32
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE };
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR };
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS };
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE };
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2,
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 };
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 };
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct analog {
10917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct input_dev *dev;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int mask;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short *buttons;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char name[ANALOG_MAX_NAME_LENGTH];
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char phys[ANALOG_MAX_PHYS_LENGTH];
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct analog_port {
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct gameport *gameport;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct analog analog[2];
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char mask;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char saitek;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cooked;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bads;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int reads;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int speed;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int loop;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int fuzz;
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int axes[4];
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buttons;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int initial[4];
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int axtime;
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Time macros.
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __i386__
138306e440daf5f40b195afd83d05dee89fa63189e7Ingo Molnar
139334955ef964bee9d3b1e20966847eee28cfd05f6Ralf Baechle#include <linux/i8253.h>
140306e440daf5f40b195afd83d05dee89fa63189e7Ingo Molnar
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GET_TIME(x)	do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0)
14218b08c55a9b04c8783420fb6657599ad724459ccDeepak Saxena#define DELTA(x,y)	(cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0)))
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TIME_NAME	(cpu_has_tsc?"TSC":"PIT")
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int get_time_pit(void)
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        unsigned long flags;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        unsigned int count;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
149ced918eb748ce30b3aace549fd17540e40ffdca0Thomas Gleixner        raw_spin_lock_irqsave(&i8253_lock, flags);
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        outb_p(0x00, 0x43);
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        count = inb_p(0x40);
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        count |= inb_p(0x40) << 8;
153ced918eb748ce30b3aace549fd17540e40ffdca0Thomas Gleixner        raw_spin_unlock_irqrestore(&i8253_lock, flags);
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        return count;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(__x86_64__)
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GET_TIME(x)	rdtscl(x)
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DELTA(x,y)	((y)-(x))
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TIME_NAME	"TSC"
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(__alpha__)
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GET_TIME(x)	do { x = get_cycles(); } while (0)
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DELTA(x,y)	((y)-(x))
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TIME_NAME	"PCC"
165b920de1b77b72ca9432ac3f97edb26541e65e5ddDavid Howells#elif defined(CONFIG_MN10300)
166b920de1b77b72ca9432ac3f97edb26541e65e5ddDavid Howells#define GET_TIME(x)	do { x = get_cycles(); } while (0)
167b920de1b77b72ca9432ac3f97edb26541e65e5ddDavid Howells#define DELTA(x, y)	((x) - (y))
168b920de1b77b72ca9432ac3f97edb26541e65e5ddDavid Howells#define TIME_NAME	"TSC"
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define FAKE_TIME
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned long analog_faketime = 0;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GET_TIME(x)     do { x = analog_faketime++; } while(0)
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DELTA(x,y)	((y)-(x))
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TIME_NAME	"Unreliable"
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#warning Precise timer not defined for this architecture.
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_decode() decodes analog joystick data and reports input events.
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void analog_decode(struct analog *analog, int *axes, int *initial, int buttons)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
18417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct input_dev *dev = analog->dev;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_HAT_FCS)
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 4; i++)
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) {
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				buttons |= 1 << (i + 14);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = j = 0; i < 6; i++)
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (analog->mask & (0x10 << i))
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1);
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_HBTN_CHF)
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 4; i++)
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1);
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_BTN_TL)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1));
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_BTN_TR)
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1));
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_BTN_TL2)
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1)));
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_BTN_TR2)
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1)));
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = j = 0; i < 4; i++)
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (analog->mask & (1 << i))
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			input_report_abs(dev, analog_axes[j++], axes[i]);
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = j = 0; i < 3; i++)
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (analog->mask & analog_exts[i]) {
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			input_report_abs(dev, analog_hats[j++],
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1));
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			input_report_abs(dev, analog_hats[j++],
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1));
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	input_sync(dev);
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_cooked_read() reads analog joystick data.
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int analog_cooked_read(struct analog_port *port)
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct gameport *gameport = port->gameport;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int time[4], start, loop, now, loopout, timeout;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char data[4], this, last;
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j;
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	loopout = (ANALOG_LOOP_TIME * port->loop) / 1000;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timeout = ANALOG_MAX_TIME * port->speed;
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_trigger(gameport);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	GET_TIME(now);
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	start = now;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	this = port->mask;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = 0;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		loop = now;
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		last = this;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_disable();
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		this = gameport_read(gameport) & port->mask;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		GET_TIME(now);
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_restore(flags);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((last ^ this) && (DELTA(loop, now) < loopout)) {
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			data[i] = last ^ this;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			time[i] = now;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			i++;
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (this && (i < 4) && (DELTA(start, now) < timeout));
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	this <<= 4;
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (--i; i >= 0; i--) {
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		this |= data[i];
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (j = 0; j < 4; j++)
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (data[i] & (1 << j))
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -(this != port->mask);
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int analog_button_read(struct analog_port *port, char saitek, char chf)
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char u;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int t = 1, i = 0;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME);
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u = gameport_read(port->gameport);
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!chf) {
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->buttons = (~u >> 4) & 0xf;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	port->buttons = 0;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ((~u & 0xf0) && (i < 16) && t) {
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf];
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!saitek) return 0;
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		udelay(ANALOG_SAITEK_DELAY);
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = strobe;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gameport_trigger(port->gameport);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (((u = gameport_read(port->gameport)) & port->mask) && t) t--;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i++;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -(!t || (i == 16));
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_poll() repeatedly polls the Analog joysticks.
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void analog_poll(struct gameport *gameport)
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct analog_port *port = gameport_get_drvdata(gameport);
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char saitek = !!(port->analog[0].mask & ANALOG_SAITEK);
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF);
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (port->cooked) {
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons);
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (chf)
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->reads++;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!port->axtime--) {
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			port->bads -= analog_cooked_read(port);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			port->bads -= analog_button_read(port, saitek, chf);
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			port->reads++;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			port->axtime = ANALOG_AXIS_TIME - 1;
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!saitek)
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				analog_button_read(port, saitek, chf);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 2; i++)
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (port->analog[i].mask)
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			analog_decode(port->analog + i, port->axes, port->initial, port->buttons);
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_open() is a callback from the input open routine.
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int analog_open(struct input_dev *dev)
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3478715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov	struct analog_port *port = input_get_drvdata(dev);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_start_polling(port->gameport);
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_close() is a callback from the input close routine.
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void analog_close(struct input_dev *dev)
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3598715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov	struct analog_port *port = input_get_drvdata(dev);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_stop_polling(port->gameport);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_calibrate_timer() calibrates the timer and computes loop
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and timeout values for a joystick port.
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void analog_calibrate_timer(struct analog_port *port)
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct gameport *gameport = port->gameport;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int i, t, tx, t1, t2, t3;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	GET_TIME(t1);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef FAKE_TIME
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog_faketime += 830;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mdelay(1);
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	GET_TIME(t2);
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	GET_TIME(t3);
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	port->speed = DELTA(t1, t2) - DELTA(t2, t3);
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tx = ~0;
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 50; i++) {
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_save(flags);
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		GET_TIME(t1);
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); }
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		GET_TIME(t3);
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_restore(flags);
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		udelay(i);
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = DELTA(t1, t2) - DELTA(t2, t3);
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (t < tx) tx = t;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        port->loop = tx / 50;
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_name() constructs a name for an analog joystick.
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void analog_name(struct analog *analog)
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
40910ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov	snprintf(analog->name, sizeof(analog->name), "Analog %d-axis %d-button",
41010ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov		 hweight8(analog->mask & ANALOG_AXES_STD),
41110ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov		 hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
41210ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov		 hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_HATS_ALL)
41510ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov		snprintf(analog->name, sizeof(analog->name), "%s %d-hat",
41610ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov			 analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_HAT_FCS)
41910ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov		strlcat(analog->name, " FCS", sizeof(analog->name));
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_ANY_CHF)
42110ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov		strlcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF",
42210ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov			sizeof(analog->name));
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
42410ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov	strlcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick",
42510ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov		sizeof(analog->name));
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_init_device()
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
43217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic int analog_init_device(struct analog_port *port, struct analog *analog, int index)
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
43417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct input_dev *input_dev;
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j, t, v, w, x, y, z;
436127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	int error;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog_name(analog);
43910ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov	snprintf(analog->phys, sizeof(analog->phys),
44010ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov		 "%s/input%d", port->gameport->phys, index);
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
44317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	analog->dev = input_dev = input_allocate_device();
44417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (!input_dev)
44517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		return -ENOMEM;
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
44717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->name = analog->name;
44817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->phys = analog->phys;
44917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.bustype = BUS_GAMEPORT;
45017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.vendor = GAMEPORT_ID_VENDOR_ANALOG;
45117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.product = analog->mask >> 4;
45217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.version = 0x0100;
453935e658e89678a7e3427b90cd7a1c86025d95bfeDmitry Torokhov	input_dev->dev.parent = &port->gameport->dev;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4558715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov	input_set_drvdata(input_dev, port);
4568715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov
45717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->open = analog_open;
45817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->close = analog_close;
4598715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov
4607b19ada2ed3c1eccb9fe94d74b05e1428224663dJiri Slaby	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = j = 0; i < 4; i++)
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (analog->mask & (1 << i)) {
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t = analog_axes[j];
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			x = port->axes[i];
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			y = (port->axes[0] + port->axes[1]) >> 1;
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			z = y - port->axes[i];
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			z = z > 0 ? z : -z;
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			v = (x >> 3);
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			w = (x >> 3);
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3)))
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				x = y;
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (analog->mask & ANALOG_SAITEK) {
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (i == 2) x = port->axes[i];
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				v = x - (x >> 2);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				w = (x >> 4);
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
48217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			input_set_abs_params(input_dev, t, v, (x << 1) - v, port->fuzz, w);
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			j++;
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = j = 0; i < 3; i++)
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (analog->mask & analog_exts[i])
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (x = 0; x < 2; x++) {
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				t = analog_hats[j++];
49017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov				input_set_abs_params(input_dev, t, -1, 1, 0, 0);
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = j = 0; i < 4; i++)
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (analog->mask & (0x10 << i))
49517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			set_bit(analog->buttons[j++], input_dev->keybit);
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_BTNS_CHF)
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 2; i++)
49917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			set_bit(analog->buttons[j++], input_dev->keybit);
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (analog->mask & ANALOG_HBTN_CHF)
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 4; i++)
50317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			set_bit(analog->buttons[j++], input_dev->keybit);
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 4; i++)
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (analog->mask & (ANALOG_BTN_TL << i))
50717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			set_bit(analog_pads[i], input_dev->keybit);
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog_decode(analog, port->axes, port->initial, port->buttons);
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	error = input_register_device(analog->dev);
512127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	if (error) {
513127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov		input_free_device(analog->dev);
514127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov		return error;
515127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	}
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	return 0;
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * analog_init_devices() sets up device-specific values and registers the input devices.
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int analog_init_masks(struct analog_port *port)
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct analog *analog = port->analog;
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int max[4];
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!port->mask)
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((port->mask & 3) != 3 && port->mask != 0xc) {
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "analog.c: Unknown joystick device found  "
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"(data=%#x, %s), probably not analog joystick.\n",
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			port->mask, port->gameport->phys);
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = analog_options[0]; /* FIXME !!! - need to specify options for different ports */
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog[0].mask = i & 0xfffff;
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD)
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			| port->mask | ((port->mask << 8) & ANALOG_HAT_FCS)
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			| ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2);
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog[0].mask &= ~(ANALOG_HAT2_CHF)
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			| ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF);
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2)
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			| ((~analog[0].mask & ANALOG_HAT_FCS) >> 8)
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			| ((~analog[0].mask & ANALOG_HAT_FCS) << 2)
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			| ((~analog[0].mask & ANALOG_HAT_FCS) << 4);
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER)
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			| (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10)
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			&  ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12));
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000);
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			: (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD);
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (port->cooked) {
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1;
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1;
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1;
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gameport_calibrate(port->gameport, port->axes, max);
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 4; i++)
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->initial[i] = port->axes[i];
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -!(analog[0].mask || analog[1].mask);
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int analog_init_port(struct gameport *gameport, struct gameport_driver *drv, struct analog_port *port)
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, t, u, v;
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	port->gameport = gameport;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_set_drvdata(gameport, port);
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) {
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		analog_calibrate_timer(port);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gameport_trigger(gameport);
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = gameport_read(gameport);
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		msleep(ANALOG_MAX_TIME);
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->mask = (gameport_read(gameport) ^ t) & t & 0xf;
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < ANALOG_INIT_RETRIES; i++) {
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!analog_cooked_read(port))
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msleep(ANALOG_MAX_TIME);
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u = v = 0;
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		msleep(ANALOG_MAX_TIME);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = gameport_time(gameport, ANALOG_MAX_TIME * 1000);
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gameport_trigger(gameport);
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while ((gameport_read(port->gameport) & port->mask) && (u < t))
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			u++;
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		udelay(ANALOG_SAITEK_DELAY);
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t = gameport_time(gameport, ANALOG_SAITEK_TIME);
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gameport_trigger(gameport);
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while ((gameport_read(port->gameport) & port->mask) && (v < t))
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			v++;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (v < (u >> 1)) { /* FIXME - more than one port */
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			analog_options[0] |= /* FIXME - more than one port */
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF;
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gameport_close(gameport);
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) {
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < ANALOG_INIT_RETRIES; i++)
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!gameport_cooked_read(gameport, port->axes, &port->buttons))
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 4; i++)
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (port->axes[i] != -1)
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				port->mask |= 1 << i;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->fuzz = gameport->fuzz;
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->cooked = 1;
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int analog_connect(struct gameport *gameport, struct gameport_driver *drv)
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct analog_port *port;
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
654a97e148a8b8da8b04bc3e18ceb824a8f5f56d567Pekka Enberg	if (!(port = kzalloc(sizeof(struct analog_port), GFP_KERNEL)))
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return - ENOMEM;
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = analog_init_port(gameport, drv, port);
65817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (err)
65917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		goto fail1;
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = analog_init_masks(port);
66217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (err)
66317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		goto fail2;
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_set_poll_handler(gameport, analog_poll);
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_set_poll_interval(gameport, 10);
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 2; i++)
66917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (port->analog[i].mask) {
67017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			err = analog_init_device(port, port->analog + i, i);
67117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			if (err)
67217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov				goto fail3;
67317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		}
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
67617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
67717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov fail3: while (--i >= 0)
678127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov		if (port->analog[i].mask)
679127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov			input_unregister_device(port->analog[i].dev);
68017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov fail2:	gameport_close(gameport);
68117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov fail1:	gameport_set_drvdata(gameport, NULL);
68217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	kfree(port);
68317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	return err;
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void analog_disconnect(struct gameport *gameport)
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct analog_port *port = gameport_get_drvdata(gameport);
68917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int i;
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 2; i++)
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (port->analog[i].mask)
69317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			input_unregister_device(port->analog[i].dev);
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_close(gameport);
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_set_drvdata(gameport, NULL);
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on %s failed\n",
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0,
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->gameport->phys);
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(port);
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct analog_types {
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *name;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int value;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct analog_types analog_types[] = {
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "none",	0x00000000 },
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "auto",	0x000000ff },
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "2btn",	0x0000003f },
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "y-joy",	0x0cc00033 },
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "y-pad",	0x8cc80033 },
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "fcs",	0x000008f7 },
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "chf",	0x000002ff },
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "fullchf",	0x000007ff },
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "gamepad",	0x000830f3 },
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "gamepad8",	0x0008f0f3 },
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ NULL, 0 }
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void analog_parse_options(void)
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j;
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *end;
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < js_nargs; i++) {
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (j = 0; analog_types[j].name; j++)
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!strcmp(analog_types[j].name, js[i])) {
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				analog_options[i] = analog_types[j].value;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (analog_types[j].name) continue;
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		analog_options[i] = simple_strtoul(js[i], &end, 0);
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (end != js[i]) continue;
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		analog_options[i] = 0xff;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!strlen(js[i])) continue;
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]);
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (; i < ANALOG_PORTS; i++)
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		analog_options[i] = 0xff;
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The gameport device structure.
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct gameport_driver analog_drv = {
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver		= {
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name	= "analog",
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.description	= DRIVER_DESC,
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.connect	= analog_connect,
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect	= analog_disconnect,
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init analog_init(void)
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	analog_parse_options();
7642547203d583cc267b98f518d5d93e3a0469d8f62Dmitry Torokhov	return gameport_register_driver(&analog_drv);
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit analog_exit(void)
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_unregister_driver(&analog_drv);
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(analog_init);
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(analog_exit);
774