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