11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) 2001 Arndt Schoenewald
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) 2000-2001 Vojtech Pavlik
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) 2000 Mark Fletcher
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Driver to use Handykey's Twiddler (the first edition, i.e. the one with
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the RS232 interface) as a joystick under Linux
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The Twiddler is a one-handed chording keyboard featuring twelve buttons on
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the front, six buttons on the top, and a built-in tilt sensor. The buttons
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * on the front, which are grouped as four rows of three buttons, are pressed
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * by the four fingers (this implies only one button per row can be held down
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * at the same time) and the buttons on the top are for the thumb. The tilt
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * sensor delivers X and Y axis data depending on how the Twiddler is held.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Additional information can be found at http://www.handykey.com.
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This driver does not use the Twiddler for its intended purpose, i.e. as
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a chording keyboard, but as a joystick: pressing and releasing a button
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * immediately sends a corresponding button event, and tilting it generates
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * controller with amazing 18 buttons :-)
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: The Twiddler2 (the successor of the Twiddler that connects directly
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to the PS/2 keyboard and mouse ports) is NOT supported by this driver!
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * For questions or feedback regarding this driver module please contact:
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Arndt Schoenewald <arndt@quelltext.com>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/input.h>
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serio.h>
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC	"Handykey Twiddler keyboard as a joystick driver"
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(DRIVER_DESC);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Constants.
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TWIDJOY_MAX_LENGTH 5
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct twidjoy_button_spec {
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bitshift;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bitmask;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buttons[3];
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldstwidjoy_buttons[] = {
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{  0, 3, { BTN_A,      BTN_B,     BTN_C    } },
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{  2, 3, { BTN_X,      BTN_Y,     BTN_Z    } },
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{  4, 3, { BTN_TL,     BTN_TR,    BTN_TR2  } },
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{  6, 3, { BTN_SELECT, BTN_START, BTN_MODE } },
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{  8, 1, { BTN_BASE5                       } },
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{  9, 1, { BTN_BASE                        } },
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 10, 1, { BTN_BASE3                       } },
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 11, 1, { BTN_BASE4                       } },
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 12, 1, { BTN_BASE2                       } },
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 13, 1, { BTN_BASE6                       } },
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0,  0, { 0                               } }
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Per-Twiddler data.
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct twidjoy {
9217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct input_dev *dev;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int idx;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char data[TWIDJOY_MAX_LENGTH];
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char phys[32];
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * twidjoy_process_packet() decodes packets the driver receives from the
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Twiddler. It updates the data accordingly.
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1037d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void twidjoy_process_packet(struct twidjoy *twidjoy)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct input_dev *dev = twidjoy->dev;
10617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	unsigned char *data = twidjoy->data;
10717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct twidjoy_button_spec *bp;
10817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int button_bits, abs_x, abs_y;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f);
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	for (bp = twidjoy_buttons; bp->bitmask; bp++) {
11317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift;
11417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		int i;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov		for (i = 0; i < bp->bitmask; i++)
11717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			input_report_key(dev, bp->buttons[i], i+1 == value);
11817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2);
12117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (data[4] & 0x08) abs_x -= 256;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0);
12417dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (data[3] & 0x02) abs_y -= 256;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_report_abs(dev, ABS_X, -abs_x);
12717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_report_abs(dev, ABS_Y, +abs_y);
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_sync(dev);
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * twidjoy_interrupt() is called by the low level driver when characters
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * are ready for us. We then buffer them for further processing, or call the
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * packet processing routine.
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1387d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct twidjoy *twidjoy = serio_get_drvdata(serio);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* All Twiddler packets are 5 bytes. The fact that the first byte
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * has a MSB of 0 and all other bytes have a MSB of 1 can be used
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * to check and regain sync. */
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((data & 0x80) == 0)
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		twidjoy->idx = 0;	/* this byte starts a new packet */
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (twidjoy->idx == 0)
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IRQ_HANDLED;	/* wrong MSB -- ignore this byte */
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (twidjoy->idx < TWIDJOY_MAX_LENGTH)
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		twidjoy->data[twidjoy->idx++] = data;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
1557d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells		twidjoy_process_packet(twidjoy);
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		twidjoy->idx = 0;
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * twidjoy_disconnect() is the opposite of twidjoy_connect()
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void twidjoy_disconnect(struct serio *serio)
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct twidjoy *twidjoy = serio_get_drvdata(serio);
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_close(serio);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_set_drvdata(serio, NULL);
17217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_unregister_device(twidjoy->dev);
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(twidjoy);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * twidjoy_connect() is the routine that is called when someone adds a
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * new serio device. It looks for the Twiddler, and if found, registers
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it as an input device.
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int twidjoy_connect(struct serio *serio, struct serio_driver *drv)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct twidjoy_button_spec *bp;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct twidjoy *twidjoy;
18617dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	struct input_dev *input_dev;
18717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	int err = -ENOMEM;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	twidjoy = kzalloc(sizeof(struct twidjoy), GFP_KERNEL);
19117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev = input_allocate_device();
19217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (!twidjoy || !input_dev)
193127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov		goto fail1;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	twidjoy->dev = input_dev;
19610ca4c0a622a942e55dc8a6d57ebd441089c9e38Dmitry Torokhov	snprintf(twidjoy->phys, sizeof(twidjoy->phys), "%s/input0", serio->phys);
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->name = "Handykey Twiddler";
19917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->phys = twidjoy->phys;
20017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.bustype = BUS_RS232;
20117dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.vendor = SERIO_TWIDJOY;
20217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.product = 0x0001;
20317dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_dev->id.version = 0x0100;
204935e658e89678a7e3427b90cd7a1c86025d95bfeDmitry Torokhov	input_dev->dev.parent = &serio->dev;
20517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
2067b19ada2ed3c1eccb9fe94d74b05e1428224663dJiri Slaby	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
20717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_set_abs_params(input_dev, ABS_X, -50, 50, 4, 4);
20817dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	input_set_abs_params(input_dev, ABS_Y, -50, 50, 4, 4);
20917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
21017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	for (bp = twidjoy_buttons; bp->bitmask; bp++)
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < bp->bitmask; i++)
21217dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov			set_bit(bp->buttons[i], input_dev->keybit);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_set_drvdata(serio, twidjoy);
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = serio_open(serio, drv);
21717dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	if (err)
218127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov		goto fail2;
219127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov
220127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	err = input_register_device(twidjoy->dev);
221127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov	if (err)
222127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov		goto fail3;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
22517dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov
226127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov fail3:	serio_close(serio);
227127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov fail2:	serio_set_drvdata(serio, NULL);
228127278ce2254c61f1346500374d61e33f74a8729Dmitry Torokhov fail1:	input_free_device(input_dev);
22917dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	kfree(twidjoy);
23017dd3f0f7aa729a042af5d3318ff9b3e7781b45bDmitry Torokhov	return err;
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The serio driver structure.
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct serio_device_id twidjoy_serio_ids[] = {
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.type	= SERIO_RS232,
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.proto	= SERIO_TWIDJOY,
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.id	= SERIO_ANY,
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.extra	= SERIO_ANY,
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0 }
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(serio, twidjoy_serio_ids);
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct serio_driver twidjoy_drv = {
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver		= {
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name	= "twidjoy",
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.description	= DRIVER_DESC,
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table	= twidjoy_serio_ids,
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.interrupt	= twidjoy_interrupt,
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.connect	= twidjoy_connect,
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect	= twidjoy_disconnect,
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The functions for inserting/removing us as a module.
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
264ffc6b529e8c46c73827008c7406f43482d71beedAdrian Bunkstatic int __init twidjoy_init(void)
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
266153a9df01c0d1ecdc56161c7a0f830325145dd64Akinobu Mita	return serio_register_driver(&twidjoy_drv);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
269ffc6b529e8c46c73827008c7406f43482d71beedAdrian Bunkstatic void __exit twidjoy_exit(void)
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_unregister_driver(&twidjoy_drv);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(twidjoy_init);
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(twidjoy_exit);
276