grip_mp.c revision 935e658e89678a7e3427b90cd7a1c86025d95bfe
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * $Id: grip_mp.c,v 1.9 2002/07/20 19:28:45 bonnland Exp $
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Driver for the Gravis Grip Multiport, a gamepad "hub" that
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  connects up to four 9-pin digital gamepads/joysticks.
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Driver tested on SMP and UP kernel versions 2.4.18-4 and 2.4.18-5.
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Thanks to Chris Gassib for helpful advice.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c)      2002 Brian Bonnlander, Bill Soudan
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) 1998-2000 Vojtech Pavlik
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/gameport.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/input.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/proc_fs.h>
224e57b6817880946a3a78d5d8cad1ace363f7e449Tim Schmielau#include <linux/jiffies.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC	"Gravis Grip Multiport driver"
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Brian Bonnlander");
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(DRIVER_DESC);
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef GRIP_DEBUG
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define dbg(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define dbg(format, arg...) do {} while (0)
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov#define GRIP_MAX_PORTS	4
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Grip multiport state
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstruct grip_port {
4217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct input_dev *dev;
4317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int mode;
4417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int registered;
4517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
4617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	/* individual gamepad states */
4717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int buttons;
4817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int xaxes;
4917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int yaxes;
5017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int dirty;     /* has the state been updated? */
5117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov};
5217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct grip_mp {
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct gameport *gameport;
5517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct grip_port *port[GRIP_MAX_PORTS];
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int reads;
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bads;
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Multiport packet interpretation
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PACKET_FULL          0x80000000       /* packet is full                        */
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PACKET_IO_FAST       0x40000000       /* 3 bits per gameport read              */
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PACKET_IO_SLOW       0x20000000       /* 1 bit per gameport read               */
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PACKET_MP_MORE       0x04000000       /* multiport wants to send more          */
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PACKET_MP_DONE       0x02000000       /* multiport done sending                */
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Packet status code interpretation
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IO_GOT_PACKET        0x0100           /* Got a packet                           */
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IO_MODE_FAST         0x0200           /* Used 3 data bits per gameport read     */
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IO_SLOT_CHANGE       0x0800           /* Multiport physical slot status changed */
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IO_DONE              0x1000           /* Multiport is done sending packets      */
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IO_RETRY             0x4000           /* Try again later to get packet          */
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IO_RESET             0x8000           /* Force multiport to resend all packets  */
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Gamepad configuration data.  Other 9-pin digital joystick devices
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * may work with the multiport, so this may not be an exhaustive list!
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Commodore 64 joystick remains untested.
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GRIP_INIT_DELAY         2000          /*  2 ms */
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GRIP_MODE_NONE		0
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GRIP_MODE_RESET         1
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GRIP_MODE_GP		2
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define GRIP_MODE_C64		3
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic const int grip_btn_gp[]  = { BTN_TR, BTN_TL, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, -1 };
9517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic const int grip_btn_c64[] = { BTN_JOYSTICK, -1 };
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic const int grip_abs_gp[]  = { ABS_X, ABS_Y, -1 };
9817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic const int grip_abs_c64[] = { ABS_X, ABS_Y, -1 };
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic const int *grip_abs[] = { NULL, NULL, grip_abs_gp, grip_abs_c64 };
10117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic const int *grip_btn[] = { NULL, NULL, grip_btn_gp, grip_btn_c64 };
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic const char *grip_name[] = { NULL, NULL, "Gravis Grip Pad", "Commodore 64 Joystick" };
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const int init_seq[] = {
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1 };
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Maps multiport directional values to X,Y axis values (each axis encoded in 3 bits) */
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic const int axis_map[] = { 5, 9, 1, 5, 6, 10, 2, 6, 4, 8, 0, 4, 5, 9, 1, 5 };
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic int register_slot(int i, struct grip_mp *grip);
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns whether an odd or even number of bits are on in pkt.
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int bit_parity(u32 pkt)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int x = pkt ^ (pkt >> 16);
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	x ^= x >> 8;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	x ^= x >> 4;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	x ^= x >> 2;
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	x ^= x >> 1;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return x & 1;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Poll gameport; return true if all bits set in 'onbits' are on and
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * all bits set in 'offbits' are off.
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int poll_until(u8 onbits, u8 offbits, int u_sec, struct gameport* gp, u8 *data)
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, nloops;
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nloops = gameport_time(gp, u_sec);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < nloops; i++) {
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*data = gameport_read(gp);
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((*data & onbits) == onbits &&
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (~(*data) & offbits) == offbits)
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 1;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("gameport timed out after %d microseconds.\n", u_sec);
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Gets a 28-bit packet from the multiport.
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * After getting a packet successfully, commands encoded by sendcode may
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * be sent to the multiport.
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The multiport clock value is reflected in gameport bit B4.
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns a packet status code indicating whether packet is valid, the transfer
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mode, and any error conditions.
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * sendflags:      current I/O status
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * sendcode:   data to send to the multiport if sendflags is nonzero
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mp_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8  raw_data;            /* raw data from gameport */
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8  data_mask;           /* packet data bits from raw_data */
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 pkt;                 /* packet temporary storage */
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bits_per_read;       /* num packet bits per gameport read */
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int portvals = 0;        /* used for port value sanity check */
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Gameport bits B0, B4, B5 should first be off, then B4 should come on. */
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*packet = 0;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	raw_data = gameport_read(gameport);
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (raw_data & 1)
180ab0c3443ad2de03383f2549195badf64779d08a1Dmitry Torokhov		return IO_RETRY;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 64; i++) {
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		raw_data = gameport_read(gameport);
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		portvals |= 1 << ((raw_data >> 4) & 3); /* Demux B4, B5 */
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (portvals == 1) {                            /* B4, B5 off */
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		raw_data = gameport_read(gameport);
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		portvals = raw_data & 0xf0;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (raw_data & 0x31)
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return IO_RESET;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gameport_trigger(gameport);
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!poll_until(0x10, 0, 308, gameport, &raw_data))
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return IO_RESET;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_RETRY;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Determine packet transfer mode and prepare for packet construction. */
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (raw_data & 0x20) {                 /* 3 data bits/read */
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		portvals |= raw_data >> 4;     /* Compare B4-B7 before & after trigger */
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (portvals != 0xb)
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_mask = 7;
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		bits_per_read = 3;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pkt = (PACKET_FULL | PACKET_IO_FAST) >> 28;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {                                 /* 1 data bit/read */
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data_mask = 1;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		bits_per_read = 1;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pkt = (PACKET_FULL | PACKET_IO_SLOW) >> 28;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Construct a packet.  Final data bits must be zero. */
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (1) {
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!poll_until(0, 0x10, 77, gameport, &raw_data))
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return IO_RESET;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		raw_data = (raw_data >> 5) & data_mask;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pkt & PACKET_FULL)
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pkt = (pkt << bits_per_read) | raw_data;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!poll_until(0x10, 0, 77, gameport, &raw_data))
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return IO_RESET;
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (raw_data)
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_RESET;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* If 3 bits/read used, drop from 30 bits to 28. */
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bits_per_read == 3) {
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pkt = (pkt & 0xffff0000) | ((pkt << 1) & 0xffff);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pkt = (pkt >> 2) | 0xf0000000;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bit_parity(pkt) == 1)
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_RESET;
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Acknowledge packet receipt */
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!poll_until(0x30, 0, 77, gameport, &raw_data))
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_RESET;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	raw_data = gameport_read(gameport);
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (raw_data & 1)
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_RESET;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_trigger(gameport);
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!poll_until(0, 0x20, 77, gameport, &raw_data))
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_RESET;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /* Return if we just wanted the packet or multiport wants to send more */
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*packet = pkt;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((sendflags == 0) || ((sendflags & IO_RETRY) && !(pkt & PACKET_MP_DONE)))
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_GOT_PACKET;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (pkt & PACKET_MP_MORE)
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_GOT_PACKET | IO_RETRY;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Multiport is done sending packets and is ready to receive data */
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!poll_until(0x20, 0, 77, gameport, &raw_data))
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_GOT_PACKET | IO_RESET;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	raw_data = gameport_read(gameport);
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (raw_data & 1)
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IO_GOT_PACKET | IO_RESET;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Trigger gameport based on bits in sendcode */
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_trigger(gameport);
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!poll_until(0x20, 0x10, 116, gameport, &raw_data))
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return IO_GOT_PACKET | IO_RESET;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!poll_until(0x30, 0, 193, gameport, &raw_data))
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return IO_GOT_PACKET | IO_RESET;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (raw_data & 1)
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return IO_GOT_PACKET | IO_RESET;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sendcode & 1)
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			gameport_trigger(gameport);
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sendcode >>= 1;
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (sendcode);
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IO_GOT_PACKET | IO_MODE_FAST;
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Disables and restores interrupts for mp_io(), which does the actual I/O.
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int multiport_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = mp_io(gameport, sendflags, sendcode, packet);
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return status;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Puts multiport into digital mode.  Multiport LED turns green.
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns true if a valid digital packet was received, false otherwise.
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dig_mode_start(struct gameport *gameport, u32 *packet)
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, seq_len = sizeof(init_seq)/sizeof(int);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int flags, tries = 0, bads = 0;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < seq_len; i++) {     /* Send magic sequence */
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (init_seq[i])
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			gameport_trigger(gameport);
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		udelay(GRIP_INIT_DELAY);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 16; i++)            /* Wait for multiport to settle */
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		udelay(GRIP_INIT_DELAY);
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (tries < 64 && bads < 8) {    /* Reset multiport and try getting a packet */
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		flags = multiport_io(gameport, IO_RESET, 0x27, packet);
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (flags & IO_MODE_FAST)
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 1;
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (flags & IO_RETRY)
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			tries++;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bads++;
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Packet structure: B0-B15   => gamepad state
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *                   B16-B20  => gamepad device type
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *                   B21-B24  => multiport slot index (1-4)
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Known device types: 0x1f (grip pad), 0x0 (no device).  Others may exist.
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns the packet status.
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_and_decode_packet(struct grip_mp *grip, int flags)
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
36217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct grip_port *port;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 packet;
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int joytype = 0;
36517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int slot;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Get a packet and check for validity */
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	flags &= IO_RESET | IO_RETRY;
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	flags = multiport_io(grip->gameport, flags, 0, &packet);
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	grip->reads++;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (packet & PACKET_MP_DONE)
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		flags |= IO_DONE;
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (flags && !(flags & IO_GOT_PACKET)) {
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		grip->bads++;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return flags;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Ignore non-gamepad packets, e.g. multiport hardware version */
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	slot = ((packet >> 21) & 0xf) - 1;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((slot < 0) || (slot > 3))
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return flags;
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
38717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	port = grip->port[slot];
38817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Handle "reset" packets, which occur at startup, and when gamepads
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * are removed or plugged in.  May contain configuration of a new gamepad.
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	joytype = (packet >> 16) & 0x1f;
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!joytype) {
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (port->registered) {
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "grip_mp: removing %s, slot %d\n",
39917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			       grip_name[port->mode], slot);
40017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			input_unregister_device(port->dev);
40117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			port->registered = 0;
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("Reset: grip multiport slot %d\n", slot);
40417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		port->mode = GRIP_MODE_RESET;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		flags |= IO_SLOT_CHANGE;
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return flags;
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Interpret a grip pad packet */
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (joytype == 0x1f) {
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int dir = (packet >> 8) & 0xf;          /* eight way directional value */
41417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		port->buttons = (~packet) & 0xff;
41517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		port->yaxes = ((axis_map[dir] >> 2) & 3) - 1;
41617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		port->xaxes = (axis_map[dir] & 3) - 1;
41717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		port->dirty = 1;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
41917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (port->mode == GRIP_MODE_RESET)
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			flags |= IO_SLOT_CHANGE;
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
42217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		port->mode = GRIP_MODE_GP;
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
42417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (!port->registered) {
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dbg("New Grip pad in multiport slot %d.\n", slot);
426127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov			if (register_slot(slot, grip)) {
427127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov				port->mode = GRIP_MODE_RESET;
428127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov				port->dirty = 0;
429127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov			}
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return flags;
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Handle non-grip device codes.  For now, just print diagnostics. */
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		static int strange_code = 0;
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (strange_code != joytype) {
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "Possible non-grip pad/joystick detected.\n");
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "Got joy type 0x%x and packet 0x%x.\n", joytype, packet);
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			strange_code = joytype;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return flags;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns true if all multiport slot states appear valid.
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int slots_valid(struct grip_mp *grip)
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int flags, slot, invalid = 0, active = 0;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	flags = get_and_decode_packet(grip, 0);
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(flags & IO_GOT_PACKET))
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (slot = 0; slot < 4; slot++) {
46017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (grip->port[slot]->mode == GRIP_MODE_RESET)
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			invalid = 1;
46217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (grip->port[slot]->mode != GRIP_MODE_NONE)
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			active = 1;
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Return true if no active slot but multiport sent all its data */
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!active)
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (flags & IO_DONE) ? 1 : 0;
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Return false if invalid device code received */
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return invalid ? 0 : 1;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns whether the multiport was placed into digital mode and
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * able to communicate its state successfully.
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int multiport_init(struct grip_mp *grip)
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int dig_mode, initialized = 0, tries = 0;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 packet;
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dig_mode = dig_mode_start(grip->gameport, &packet);
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (!dig_mode && tries < 4) {
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dig_mode = dig_mode_start(grip->gameport, &packet);
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tries++;
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dig_mode)
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("multiport_init(): digital mode activated.\n");
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else {
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("multiport_init(): unable to activate digital mode.\n");
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Get packets, store multiport state, and check state's validity */
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (tries = 0; tries < 4096; tries++) {
49917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (slots_valid(grip)) {
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			initialized = 1;
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("multiport_init(): initialized == %d\n", initialized);
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return initialized;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Reports joystick state to the linux input layer.
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void report_slot(struct grip_mp *grip, int slot)
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
51417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct grip_port *port = grip->port[slot];
51517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int i;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Store button states with linux input driver */
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 8; i++)
52017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		input_report_key(port->dev, grip_btn_gp[i], (port->buttons >> i) & 1);
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Store axis states with linux driver */
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
52417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_report_abs(port->dev, ABS_X, port->xaxes);
52517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_report_abs(port->dev, ABS_Y, port->yaxes);
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Tell the receiver of the events to process them */
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
52917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_sync(port->dev);
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
53117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	port->dirty = 0;
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Get the multiport state.
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void grip_poll(struct gameport *gameport)
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct grip_mp *grip = gameport_get_drvdata(gameport);
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, npkts, flags;
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (npkts = 0; npkts < 4; npkts++) {
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		flags = IO_RETRY;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 32; i++) {
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			flags = get_and_decode_packet(grip, flags);
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((flags & IO_GOT_PACKET) || !(flags & IO_RETRY))
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (flags & IO_DONE)
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 4; i++)
55517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (grip->port[i]->dirty)
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			report_slot(grip, i);
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Called when a joystick device file is opened
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int grip_open(struct input_dev *dev)
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5658715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov	struct grip_mp *grip = input_get_drvdata(dev);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_start_polling(grip->gameport);
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Called when a joystick device file is closed
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void grip_close(struct input_dev *dev)
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5778715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov	struct grip_mp *grip = input_get_drvdata(dev);
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5798715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov	gameport_stop_polling(grip->gameport);
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Tell the linux input layer about a newly plugged-in gamepad.
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
58617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhovstatic int register_slot(int slot, struct grip_mp *grip)
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
58817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct grip_port *port = grip->port[slot];
58917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct input_dev *input_dev;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int j, t;
591127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	int err;
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	port->dev = input_dev = input_allocate_device();
59417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (!input_dev)
59517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		return -ENOMEM;
59617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
59717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->name = grip_name[port->mode];
59817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.bustype = BUS_GAMEPORT;
59917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
60017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.product = 0x0100 + port->mode;
60117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.version = 0x0100;
602935e658e89678a7e3427b90cd7a1c86025d95bfeDmitry Torokhov	input_dev->dev.parent = &grip->gameport->dev;
6038715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov
6048715c1cfadf8cce24e79d254f95bd4a84c7741f0Dmitry Torokhov	input_set_drvdata(input_dev, grip);
60517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
60617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->open = grip_open;
60717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->close = grip_close;
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
60917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	for (j = 0; (t = grip_abs[port->mode][j]) >= 0; j++)
61217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		input_set_abs_params(input_dev, t, -1, 1, 0, 0);
61317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
61417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	for (j = 0; (t = grip_btn[port->mode][j]) >= 0; j++)
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (t > 0)
61617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			set_bit(t, input_dev->keybit);
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
618127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	err = input_register_device(port->dev);
619127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	if (err) {
620127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov		input_free_device(port->dev);
621127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov		return err;
622127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	}
623127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov
62417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	port->registered = 1;
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
62617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (port->dirty)	            /* report initial state, if any */
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		report_slot(grip, slot);
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
62917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	return 0;
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct grip_mp *grip;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
637a97e148a8b8da8b04bc3e18ceb824a8f5f56d567Pekka Enberg	if (!(grip = kzalloc(sizeof(struct grip_mp), GFP_KERNEL)))
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	grip->gameport = gameport;
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_set_drvdata(gameport, grip);
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail1;
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_set_poll_handler(gameport, grip_poll);
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_set_poll_interval(gameport, 20);
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!multiport_init(grip)) {
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -ENODEV;
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail2;
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
65617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (!grip->port[0]->mode && !grip->port[1]->mode && !grip->port[2]->mode && !grip->port[3]->mode) {
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* nothing plugged in */
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -ENODEV;
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail2;
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfail2:	gameport_close(gameport);
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfail1:	gameport_set_drvdata(gameport, NULL);
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(grip);
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void grip_disconnect(struct gameport *gameport)
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct grip_mp *grip = gameport_get_drvdata(gameport);
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 4; i++)
67617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		if (grip->port[i]->registered)
67717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			input_unregister_device(grip->port[i]->dev);
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_close(gameport);
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_set_drvdata(gameport, NULL);
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(grip);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct gameport_driver grip_drv = {
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver		= {
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name	= "grip_mp",
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.description	= DRIVER_DESC,
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.connect	= grip_connect,
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect	= grip_disconnect,
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init grip_init(void)
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_register_driver(&grip_drv);
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit grip_exit(void)
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gameport_unregister_driver(&grip_drv);
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(grip_init);
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(grip_exit);
705