summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorJames Simmons <jsimmons@heisenberg.transvirtual.com>2002-07-07 22:56:15 -0700
committerJames Simmons <jsimmons@heisenberg.transvirtual.com>2002-07-07 22:56:15 -0700
commit8ef1bf6df837a5f92e2d8d50ca26c77998602ff4 (patch)
tree4b08ab0d040f739937b98081fae5cc7aa1b39c64 /drivers/input
parentaf5c826ce279f0cf9f87ce7543e94b0d3b83a644 (diff)
parenta321a55fcbb2c21eb7bc8b7d4b294eefaea9065c (diff)
Merge http://fbdev.bkbits.net/fbdev-2.5
into heisenberg.transvirtual.com:/tmp/fbdev-2.5
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Config.help26
-rw-r--r--drivers/input/Config.in14
-rw-r--r--drivers/input/Makefile6
-rw-r--r--drivers/input/evbug.c99
-rw-r--r--drivers/input/evdev.c18
-rw-r--r--drivers/input/gameport/Config.help12
-rw-r--r--drivers/input/gameport/Config.in2
-rw-r--r--drivers/input/gameport/Makefile3
-rw-r--r--drivers/input/gameport/fm801-gp.c162
-rw-r--r--drivers/input/gameport/gameport.c28
-rw-r--r--drivers/input/gameport/pcigame.c199
-rw-r--r--drivers/input/gameport/vortex.c185
-rw-r--r--drivers/input/input.c14
-rw-r--r--drivers/input/joydev.c10
-rw-r--r--drivers/input/joystick/Config.help29
-rw-r--r--drivers/input/joystick/Config.in4
-rw-r--r--drivers/input/joystick/Makefile6
-rw-r--r--drivers/input/joystick/adi.c4
-rw-r--r--drivers/input/joystick/db9.c12
-rw-r--r--drivers/input/joystick/gamecon.c6
-rw-r--r--drivers/input/joystick/guillemot.c285
-rw-r--r--drivers/input/joystick/iforce.c1224
-rw-r--r--drivers/input/joystick/iforce/Makefile42
-rw-r--r--drivers/input/joystick/iforce/iforce-ff.c543
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c543
-rw-r--r--drivers/input/joystick/iforce/iforce-packets.c314
-rw-r--r--drivers/input/joystick/iforce/iforce-serio.c166
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c214
-rw-r--r--drivers/input/joystick/iforce/iforce.h194
-rw-r--r--drivers/input/joystick/joydump.c152
-rw-r--r--drivers/input/joystick/magellan.c2
-rw-r--r--drivers/input/joystick/twidjoy.c264
-rw-r--r--drivers/input/keybdev.c4
-rw-r--r--drivers/input/keyboard/Config.help66
-rw-r--r--drivers/input/keyboard/Config.in18
-rw-r--r--drivers/input/keyboard/Makefile16
-rw-r--r--drivers/input/keyboard/amikbd.c144
-rw-r--r--drivers/input/keyboard/atkbd.c565
-rw-r--r--drivers/input/keyboard/maple_keyb.c190
-rw-r--r--drivers/input/keyboard/ps2serkbd.c298
-rw-r--r--drivers/input/keyboard/sunkbd.c318
-rw-r--r--drivers/input/keyboard/xtkbd.c157
-rw-r--r--drivers/input/mouse/Config.help87
-rw-r--r--drivers/input/mouse/Config.in24
-rw-r--r--drivers/input/mouse/Makefile18
-rw-r--r--drivers/input/mouse/amimouse.c147
-rw-r--r--drivers/input/mouse/inport.c193
-rw-r--r--drivers/input/mouse/logibm.c182
-rw-r--r--drivers/input/mouse/maplemouse.c137
-rw-r--r--drivers/input/mouse/pc110pad.c163
-rw-r--r--drivers/input/mouse/psmouse.c652
-rw-r--r--drivers/input/mouse/rpcmouse.c111
-rw-r--r--drivers/input/mouse/sermouse.c299
-rw-r--r--drivers/input/mousedev.c27
-rw-r--r--drivers/input/power.c180
-rw-r--r--drivers/input/serio/Config.help47
-rw-r--r--drivers/input/serio/Config.in12
-rw-r--r--drivers/input/serio/Makefile4
-rw-r--r--drivers/input/serio/ct82c710.c212
-rw-r--r--drivers/input/serio/i8042.c717
-rw-r--r--drivers/input/serio/i8042.h128
-rw-r--r--drivers/input/serio/parkbd.c203
-rw-r--r--drivers/input/serio/rpckbd.c117
-rw-r--r--drivers/input/touchscreen/Config.help16
-rw-r--r--drivers/input/touchscreen/Config.in7
-rw-r--r--drivers/input/touchscreen/Makefile11
-rw-r--r--drivers/input/touchscreen/gunze.c178
-rw-r--r--drivers/input/tsdev.c443
68 files changed, 9398 insertions, 1475 deletions
diff --git a/drivers/input/Config.help b/drivers/input/Config.help
index 15bc1aab59bd..e75624d0917d 100644
--- a/drivers/input/Config.help
+++ b/drivers/input/Config.help
@@ -64,6 +64,18 @@ CONFIG_INPUT_JOYDEV
The module will be called joydev.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+CONFIG_INPUT_TSDEV
+ Say Y here if you have an application that only can understand the
+ Compaq touchscreen protocol for absolute pointer data. This is
+ useful namely for embedded configurations.
+
+ If unsure, say N.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called tsdev.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
CONFIG_INPUT_EVDEV
Say Y here if you want your input device events be accessible
under char device 13:64+ - /dev/input/eventX in a generic way.
@@ -72,3 +84,17 @@ CONFIG_INPUT_EVDEV
inserted in and removed from the running kernel whenever you want).
The module will be called evdev.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_INPUT_EVBUG
+ Say Y here if you have a problem with the input subsystem and
+ want all events (keypresses, mouse movements), to be output to
+ the system log. While this is useful for debugging, it's also
+ a security threat - your keypresses include your passwords, of
+ course.
+
+ If unsure, say N.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called joydev.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
diff --git a/drivers/input/Config.in b/drivers/input/Config.in
index 66b2aa72897e..7105ceb2f7a6 100644
--- a/drivers/input/Config.in
+++ b/drivers/input/Config.in
@@ -6,20 +6,34 @@ mainmenu_option next_comment
comment 'Input device support'
tristate 'Input core support' CONFIG_INPUT
+
+comment 'Userland interfaces'
dep_tristate ' Keyboard interface' CONFIG_INPUT_KEYBDEV $CONFIG_INPUT
dep_tristate ' Mouse interface' CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT
+dep_mbool ' Provide legacy /dev/psaux device' CONFIG_INPUT_MOUSEDEV_PSAUX $CONFIG_INPUT
if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then
int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
int ' Vertical screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768
fi
dep_tristate ' Joystick interface' CONFIG_INPUT_JOYDEV $CONFIG_INPUT
+dep_tristate ' Touchscreen interface' CONFIG_INPUT_TSDEV $CONFIG_INPUT
+if [ "$CONFIG_INPUT_TSDEV" != "n" ]; then
+ int ' Horizontal screen resolution' CONFIG_INPUT_TSDEV_SCREEN_X 240
+ int ' Vertical screen resolution' CONFIG_INPUT_TSDEV_SCREEN_Y 320
+fi
dep_tristate ' Event interface' CONFIG_INPUT_EVDEV $CONFIG_INPUT
+dep_tristate ' Event debugging' CONFIG_INPUT_EVBUG $CONFIG_INPUT
+comment 'Input I/O drivers'
source drivers/input/gameport/Config.in
source drivers/input/serio/Config.in
+comment 'Input Device Drivers'
if [ "$CONFIG_INPUT" != "n" ]; then
+ source drivers/input/keyboard/Config.in
+ source drivers/input/mouse/Config.in
source drivers/input/joystick/Config.in
+ source drivers/input/touchscreen/Config.in
fi
endmenu
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index bdb70aa4a996..d6a647876456 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -13,8 +13,14 @@ obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
+obj-$(CONFIG_INPUT_TSDEV) += tsdev.o
+obj-$(CONFIG_INPUT_POWER) += power.o
+obj-$(CONFIG_INPUT_EVBUG) += evbug.o
+obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
+obj-$(CONFIG_INPUT_MOUSE) += mouse/
obj-$(CONFIG_INPUT_JOYSTICK) += joystick/
+obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
# The global Rules.make.
diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c
new file mode 100644
index 000000000000..1b91f246cd08
--- /dev/null
+++ b/drivers/input/evbug.c
@@ -0,0 +1,99 @@
+/*
+ * $Id: evbug.c,v 1.10 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Input driver event debug module - dumps all events into syslog
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input driver event debug module");
+MODULE_LICENSE("GPL");
+
+static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+ printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", handle->dev->phys, type, code, value);
+}
+
+static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+{
+ struct input_handle *handle;
+
+ if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
+ return NULL;
+ memset(handle, 0, sizeof(struct input_handle));
+
+ handle->dev = dev;
+ handle->handler = handler;
+
+ input_open_device(handle);
+
+ printk(KERN_DEBUG "evbug.c: Connected device: \"%s\", %s\n", dev->name, dev->phys);
+
+ return handle;
+}
+
+static void evbug_disconnect(struct input_handle *handle)
+{
+ printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n", handle->dev->phys);
+
+ input_close_device(handle);
+
+ kfree(handle);
+}
+
+static struct input_device_id evbug_ids[] = {
+ { driver_info: 1 }, /* Matches all devices */
+ { }, /* Terminating zero entry */
+};
+
+MODULE_DEVICE_TABLE(input, evbug_ids);
+
+static struct input_handler evbug_handler = {
+ event: evbug_event,
+ connect: evbug_connect,
+ disconnect: evbug_disconnect,
+ name: "evbug",
+ id_table: evbug_ids,
+};
+
+int __init evbug_init(void)
+{
+ input_register_handler(&evbug_handler);
+ return 0;
+}
+
+void __exit evbug_exit(void)
+{
+ input_unregister_handler(&evbug_handler);
+}
+
+module_init(evbug_init);
+module_exit(evbug_exit);
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 413002ffa574..0a2f930cf585 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -1,5 +1,5 @@
/*
- * $Id: evdev.c,v 1.42 2002/01/02 11:59:56 vojtech Exp $
+ * $Id: evdev.c,v 1.48 2002/05/26 14:28:26 jdeneux Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
@@ -40,7 +40,6 @@
struct evdev {
int exist;
int open;
- int open_for_write;
int minor;
char name[16];
struct input_handle handle;
@@ -91,7 +90,9 @@ static int evdev_fasync(int fd, struct file *file, int on)
static int evdev_flush(struct file * file)
{
- return input_flush_device(&((struct evdev_list*)file->private_data)->evdev->handle, file);
+ struct evdev_list *list = (struct evdev_list*)file->private_data;
+ if (!list->evdev->exist) return -ENODEV;
+ return input_flush_device(&list->evdev->handle, file);
}
static int evdev_release(struct inode * inode, struct file * file)
@@ -158,6 +159,8 @@ static ssize_t evdev_write(struct file * file, const char * buffer, size_t count
struct input_event event;
int retval = 0;
+ if (!list->evdev->exist) return -ENODEV;
+
while (retval < count) {
if (copy_from_user(&event, buffer + retval, sizeof(struct input_event)))
@@ -232,6 +235,8 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
struct input_dev *dev = evdev->handle.dev;
int retval, t, u;
+ if (!evdev->exist) return -ENODEV;
+
switch (cmd) {
case EVIOCGVERSION:
@@ -284,11 +289,11 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
int err;
if (copy_from_user((void*)(&effect), (void*)arg, sizeof(effect))) {
- return -EINVAL;
+ return -EFAULT;
}
err = dev->upload_effect(dev, &effect);
if (put_user(effect.id, &(((struct ff_effect*)arg)->id))) {
- return -EINVAL;
+ return -EFAULT;
}
return err;
}
@@ -301,7 +306,8 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
else return -ENOSYS;
case EVIOCGEFFECTS:
- put_user(dev->ff_effects_max, (int*) arg);
+ if ((retval = put_user(dev->ff_effects_max, (int*) arg)))
+ return retval;
return 0;
default:
diff --git a/drivers/input/gameport/Config.help b/drivers/input/gameport/Config.help
index 54cac2482adf..10b849c461c3 100644
--- a/drivers/input/gameport/Config.help
+++ b/drivers/input/gameport/Config.help
@@ -34,7 +34,7 @@ CONFIG_GAMEPORT_L4
The module will be called lightning.o. If you want to compile it as
a module, say M here and read <file:Documentation/modules.txt>.
-CONFIG_GAMEPORT_EMU10K1
+CONFIG_INPUT_EMU10K1
Say Y here if you have a SoundBlaster Live! or SoundBlaster
Audigy card and want to use its gameport.
@@ -43,16 +43,16 @@ CONFIG_GAMEPORT_EMU10K1
The module will be called emu10k1-gp.o. If you want to compile it as
a module, say M here and read <file:Documentation/modules.txt>.
-CONFIG_GAMEPORT_PCIGAME
- Say Y here if you have an Aureal Vortex 1 or 2 or a Trident
- 4DWave NX or DX card and want to use its gameport.
+CONFIG_GAMEPORT_VORTEX
+ Say Y here if you have an Aureal Vortex 1 or 2 card and want
+ to use its gameport.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
- The module will be called pcigame.o. If you want to compile it as a
+ The module will be called vortex.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
-CONFIG_GAMEPORT_CS461X
+CONFIG_GAMEPORT_CS461x
Say Y here if you have a Cirrus CS461x aka "Crystal SoundFusion"
PCI audio accelerator and want to use its gameport.
diff --git a/drivers/input/gameport/Config.in b/drivers/input/gameport/Config.in
index df89f6a6f189..eeb983ec42a5 100644
--- a/drivers/input/gameport/Config.in
+++ b/drivers/input/gameport/Config.in
@@ -14,6 +14,6 @@ fi
dep_tristate ' Classic ISA and PnP gameport support' CONFIG_GAMEPORT_NS558 $CONFIG_GAMEPORT
dep_tristate ' PDPI Lightning 4 gamecard support' CONFIG_GAMEPORT_L4 $CONFIG_GAMEPORT
dep_tristate ' SB Live and Audigy gameport support' CONFIG_INPUT_EMU10K1 $CONFIG_GAMEPORT
-dep_tristate ' Aureal Vortex, Vortex 2 and Trident 4DWave NX/DX gameport support' CONFIG_GAMEPORT_PCIGAME $CONFIG_GAMEPORT
+dep_tristate ' Aureal Vortex, Vortex 2 gameport support' CONFIG_GAMEPORT_VORTEX $CONFIG_GAMEPORT
dep_tristate ' ForteMedia FM801 gameport support' CONFIG_GAMEPORT_FM801 $CONFIG_GAMEPORT
dep_tristate ' Crystal SoundFusion gameport support' CONFIG_GAMEPORT_CS461x $CONFIG_GAMEPORT
diff --git a/drivers/input/gameport/Makefile b/drivers/input/gameport/Makefile
index 60509253a031..ac1991701f6a 100644
--- a/drivers/input/gameport/Makefile
+++ b/drivers/input/gameport/Makefile
@@ -11,9 +11,10 @@ export-objs := gameport.o
obj-$(CONFIG_GAMEPORT) += gameport.o
obj-$(CONFIG_GAMEPORT_CS461X) += cs461x.o
obj-$(CONFIG_GAMEPORT_EMU10K1) += emu10k1-gp.o
+obj-$(CONFIG_GAMEPORT_FM801) += fm801-gp.o
obj-$(CONFIG_GAMEPORT_L4) += lightning.o
obj-$(CONFIG_GAMEPORT_NS558) += ns558.o
-obj-$(CONFIG_GAMEPORT_PCIGAME) += pcigame.o
+obj-$(CONFIG_GAMEPORT_VORTEX) += vortex.o
# The global Rules.make.
diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c
new file mode 100644
index 000000000000..7cd454338f83
--- /dev/null
+++ b/drivers/input/gameport/fm801-gp.c
@@ -0,0 +1,162 @@
+/*
+ * FM801 gameport driver for Linux
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+
+#define PCI_VENDOR_ID_FORTEMEDIA 0x1319
+#define PCI_DEVICE_ID_FM801_GP 0x0802
+
+#define HAVE_COOKED
+
+struct fm801_gp {
+ struct gameport gameport;
+ struct resource *res_port;
+ char phys[32];
+ char name[32];
+};
+
+#ifdef HAVE_COOKED
+static int fm801_gp_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ unsigned short w;
+
+ w = inw(gameport->io + 2);
+ *buttons = (~w >> 14) & 0x03;
+ axes[0] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+ w = inw(gameport->io + 4);
+ axes[1] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+ w = inw(gameport->io + 6);
+ *buttons |= ((~w >> 14) & 0x03) << 2;
+ axes[2] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+ w = inw(gameport->io + 8);
+ axes[3] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+ outw(0xff, gameport->io); /* reset */
+
+ return 0;
+}
+#endif
+
+static int fm801_gp_open(struct gameport *gameport, int mode)
+{
+ switch (mode) {
+#ifdef HAVE_COOKED
+ case GAMEPORT_MODE_COOKED:
+ return 0;
+#endif
+ case GAMEPORT_MODE_RAW:
+ return 0;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __devinit fm801_gp_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+ struct fm801_gp *gp;
+
+ if (! (gp = kmalloc(sizeof(*gp), GFP_KERNEL))) {
+ printk("cannot malloc for fm801-gp\n");
+ return -1;
+ }
+ memset(gp, 0, sizeof(*gp));
+
+ gp->gameport.open = fm801_gp_open;
+#ifdef HAVE_COOKED
+ gp->gameport.cooked_read = fm801_gp_cooked_read;
+#endif
+
+ pci_enable_device(pci);
+ gp->gameport.io = pci_resource_start(pci, 0);
+ if ((gp->res_port = request_region(gp->gameport.io, 0x10, "FM801 GP")) == NULL) {
+ kfree(gp);
+ printk("unable to grab region 0x%x-0x%x\n", gp->gameport.io, gp->gameport.io + 0x0f);
+ return -1;
+ }
+
+ gp->gameport.phys = gp->phys;
+ gp->gameport.name = gp->name;
+ gp->gameport.idbus = BUS_PCI;
+ gp->gameport.idvendor = pci->vendor;
+ gp->gameport.idproduct = pci->device;
+
+ pci_set_drvdata(pci, gp);
+
+ outb(0x60, gp->gameport.io + 0x0d); /* enable joystick 1 and 2 */
+
+ gameport_register_port(&gp->gameport);
+
+ printk(KERN_INFO "gameport: %s at pci%s speed %d kHz\n",
+ pci->name, pci->slot_name, gp->gameport.speed);
+
+ return 0;
+}
+
+static void __devexit fm801_gp_remove(struct pci_dev *pci)
+{
+ struct fm801_gp *gp = pci_get_drvdata(pci);
+ if (gp) {
+ gameport_unregister_port(&gp->gameport);
+ release_resource(gp->res_port);
+ kfree(gp);
+ }
+}
+
+static struct pci_device_id fm801_gp_id_table[] __devinitdata = {
+ { PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0 }
+};
+
+static struct pci_driver fm801_gp_driver = {
+ name: "FM801 GP",
+ id_table: fm801_gp_id_table,
+ probe: fm801_gp_probe,
+ remove: fm801_gp_remove,
+};
+
+int __init fm801_gp_init(void)
+{
+ return pci_module_init(&fm801_gp_driver);
+}
+
+void __exit fm801_gp_exit(void)
+{
+ pci_unregister_driver(&fm801_gp_driver);
+}
+
+module_init(fm801_gp_init);
+module_exit(fm801_gp_exit);
+
+MODULE_DEVICE_TABLE(pci, fm801_gp_id_table);
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index 01542530e6e4..4ade83a2db99 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -54,16 +54,36 @@ EXPORT_SYMBOL(gameport_cooked_read);
static struct gameport *gameport_list;
static struct gameport_dev *gameport_dev;
+
+#ifdef __i386__
+
+#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180/HZ:0))
+#define GET_TIME(x) do { x = get_time_pit(); } while (0)
+
+static unsigned int get_time_pit(void)
+{
+ extern spinlock_t i8253_lock;
+ unsigned long flags;
+ unsigned int count;
+
+ spin_lock_irqsave(&i8253_lock, flags);
+ outb_p(0x00, 0x43);
+ count = inb_p(0x40);
+ count |= inb_p(0x40) << 8;
+ spin_unlock_irqrestore(&i8253_lock, flags);
+
+ return count;
+}
+
+#endif
+
/*
* gameport_measure_speed() measures the gameport i/o speed.
*/
static int gameport_measure_speed(struct gameport *gameport)
{
-#if defined(__i386__) || defined(__x86_64__)
-
-#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0)
-#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0))
+#ifdef __i386__
unsigned int i, t, t1, t2, t3, tx;
unsigned long flags;
diff --git a/drivers/input/gameport/pcigame.c b/drivers/input/gameport/pcigame.c
deleted file mode 100644
index 194814ca64fc..000000000000
--- a/drivers/input/gameport/pcigame.c
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * $Id: pcigame.c,v 1.10 2001/04/26 10:24:46 vojtech Exp $
- *
- * Copyright (c) 2000-2001 Vojtech Pavlik
- *
- * Based on the work of:
- * Raymond Ingles
- *
- * Sponsored by SuSE
- */
-
-/*
- * Trident 4DWave and Aureal Vortex gameport driver for Linux
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
- */
-
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/gameport.h>
-
-#define PCI_VENDOR_ID_AUREAL 0x12eb
-
-#define PCIGAME_DATA_WAIT 20 /* 20 ms */
-
-#define PCIGAME_4DWAVE 0
-#define PCIGAME_VORTEX 1
-#define PCIGAME_VORTEX2 2
-
-struct pcigame_data {
- int gcr; /* Gameport control register */
- int legacy; /* Legacy port location */
- int axes; /* Axes start */
- int axsize; /* Axis field size */
- int axmax; /* Axis field max value */
- int adcmode; /* Value to enable ADC mode in GCR */
-};
-
-static struct pcigame_data pcigame_data[] __devinitdata =
-{{ 0x00030, 0x00031, 0x00034, 2, 0xffff, 0x80 },
- { 0x1100c, 0x11008, 0x11010, 4, 0x1fff, 0x40 },
- { 0x2880c, 0x28808, 0x28810, 4, 0x1fff, 0x40 },
- { 0 }};
-
-struct pcigame {
- struct gameport gameport;
- struct pci_dev *dev;
- unsigned char *base;
- struct pcigame_data *data;
-};
-
-static unsigned char pcigame_read(struct gameport *gameport)
-{
- struct pcigame *pcigame = gameport->private;
- return readb(pcigame->base + pcigame->data->legacy);
-}
-
-static void pcigame_trigger(struct gameport *gameport)
-{
- struct pcigame *pcigame = gameport->private;
- writeb(0xff, pcigame->base + pcigame->data->legacy);
-}
-
-static int pcigame_cooked_read(struct gameport *gameport, int *axes, int *buttons)
-{
- struct pcigame *pcigame = gameport->private;
- int i;
-
- *buttons = (~readb(pcigame->base + pcigame->data->legacy) >> 4) & 0xf;
-
- for (i = 0; i < 4; i++) {
- axes[i] = readw(pcigame->base + pcigame->data->axes + i * pcigame->data->axsize);
- if (axes[i] == pcigame->data->axmax) axes[i] = -1;
- }
-
- return 0;
-}
-
-static int pcigame_open(struct gameport *gameport, int mode)
-{
- struct pcigame *pcigame = gameport->private;
-
- switch (mode) {
- case GAMEPORT_MODE_COOKED:
- writeb(pcigame->data->adcmode, pcigame->base + pcigame->data->gcr);
- wait_ms(PCIGAME_DATA_WAIT);
- return 0;
- case GAMEPORT_MODE_RAW:
- writeb(0, pcigame->base + pcigame->data->gcr);
- return 0;
- default:
- return -1;
- }
-
- return 0;
-}
-
-static int __devinit pcigame_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
- struct pcigame *pcigame;
- int i;
-
- if (!(pcigame = kmalloc(sizeof(struct pcigame), GFP_KERNEL)))
- return -1;
- memset(pcigame, 0, sizeof(struct pcigame));
-
-
- pcigame->data = pcigame_data + id->driver_data;
-
- pcigame->dev = dev;
- pci_set_drvdata(dev, pcigame);
-
- pcigame->gameport.private = pcigame;
- pcigame->gameport.fuzz = 64;
-
- pcigame->gameport.read = pcigame_read;
- pcigame->gameport.trigger = pcigame_trigger;
- pcigame->gameport.cooked_read = pcigame_cooked_read;
- pcigame->gameport.open = pcigame_open;
-
- for (i = 0; i < 6; i++)
- if (~pci_resource_flags(dev, i) & IORESOURCE_IO)
- break;
-
- pci_enable_device(dev);
-
- pcigame->base = ioremap(pci_resource_start(pcigame->dev, i),
- pci_resource_len(pcigame->dev, i));
-
- gameport_register_port(&pcigame->gameport);
-
- printk(KERN_INFO "gameport%d: %s at pci%02x:%02x.%x speed %d kHz\n",
- pcigame->gameport.number, dev->name, dev->bus->number,
- PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), pcigame->gameport.speed);
-
- return 0;
-}
-
-static void __devexit pcigame_remove(struct pci_dev *dev)
-{
- struct pcigame *pcigame = pci_get_drvdata(dev);
- gameport_unregister_port(&pcigame->gameport);
- iounmap(pcigame->base);
- kfree(pcigame);
-}
-
-static struct pci_device_id pcigame_id_table[] __devinitdata =
-{{ PCI_VENDOR_ID_TRIDENT, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_4DWAVE },
- { PCI_VENDOR_ID_TRIDENT, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_4DWAVE },
- { PCI_VENDOR_ID_AUREAL, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_VORTEX },
- { PCI_VENDOR_ID_AUREAL, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_VORTEX2 },
- { 0 }};
-
-static struct pci_driver pcigame_driver = {
- name: "pcigame",
- id_table: pcigame_id_table,
- probe: pcigame_probe,
- remove: __devexit_p(pcigame_remove),
-};
-
-int __init pcigame_init(void)
-{
- return pci_module_init(&pcigame_driver);
-}
-
-void __exit pcigame_exit(void)
-{
- pci_unregister_driver(&pcigame_driver);
-}
-
-module_init(pcigame_init);
-module_exit(pcigame_exit);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/gameport/vortex.c b/drivers/input/gameport/vortex.c
new file mode 100644
index 000000000000..c2c12e7d4a5b
--- /dev/null
+++ b/drivers/input/gameport/vortex.c
@@ -0,0 +1,185 @@
+/*
+ * $Id: vortex.c,v 1.5 2002/07/01 15:39:30 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Raymond Ingles
+ */
+
+/*
+ * Trident 4DWave and Aureal Vortex gameport driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Aureal Vortex and Vortex2 gameport driver");
+MODULE_LICENSE("GPL");
+
+#define VORTEX_GCR 0x0c /* Gameport control register */
+#define VORTEX_LEG 0x08 /* Legacy port location */
+#define VORTEX_AXD 0x10 /* Axes start */
+#define VORTEX_DATA_WAIT 20 /* 20 ms */
+
+struct vortex {
+ struct gameport gameport;
+ struct pci_dev *dev;
+ unsigned char *base;
+ unsigned char *io;
+ char phys[32];
+};
+
+static unsigned char vortex_read(struct gameport *gameport)
+{
+ struct vortex *vortex = gameport->driver;
+ return readb(vortex->io + VORTEX_LEG);
+}
+
+static void vortex_trigger(struct gameport *gameport)
+{
+ struct vortex *vortex = gameport->driver;
+ writeb(0xff, vortex->io + VORTEX_LEG);
+}
+
+static int vortex_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ struct vortex *vortex = gameport->driver;
+ int i;
+
+ *buttons = (~readb(vortex->base + VORTEX_LEG) >> 4) & 0xf;
+
+ for (i = 0; i < 4; i++) {
+ axes[i] = readw(vortex->io + VORTEX_AXD + i * sizeof(u32));
+ if (axes[i] == 0x1fff) axes[i] = -1;
+ }
+
+ return 0;
+}
+
+static int vortex_open(struct gameport *gameport, int mode)
+{
+ struct vortex *vortex = gameport->driver;
+
+ switch (mode) {
+ case GAMEPORT_MODE_COOKED:
+ writeb(0x40, vortex->io + VORTEX_GCR);
+ wait_ms(VORTEX_DATA_WAIT);
+ return 0;
+ case GAMEPORT_MODE_RAW:
+ writeb(0x00, vortex->io + VORTEX_GCR);
+ return 0;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __devinit vortex_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct vortex *vortex;
+ int i;
+
+ if (!(vortex = kmalloc(sizeof(struct vortex), GFP_KERNEL)))
+ return -1;
+ memset(vortex, 0, sizeof(struct vortex));
+
+ vortex->dev = dev;
+ sprintf(vortex->phys, "pci%s/gameport0", dev->slot_name);
+
+ pci_set_drvdata(dev, vortex);
+
+ vortex->gameport.driver = vortex;
+ vortex->gameport.fuzz = 64;
+
+ vortex->gameport.read = vortex_read;
+ vortex->gameport.trigger = vortex_trigger;
+ vortex->gameport.cooked_read = vortex_cooked_read;
+ vortex->gameport.open = vortex_open;
+
+ vortex->gameport.name = dev->name;
+ vortex->gameport.phys = vortex->phys;
+ vortex->gameport.idbus = BUS_PCI;
+ vortex->gameport.idvendor = dev->vendor;
+ vortex->gameport.idproduct = dev->device;
+
+ for (i = 0; i < 6; i++)
+ if (~pci_resource_flags(dev, i) & IORESOURCE_IO)
+ break;
+
+ pci_enable_device(dev);
+
+ vortex->base = ioremap(pci_resource_start(vortex->dev, i),
+ pci_resource_len(vortex->dev, i));
+ vortex->io = vortex->base + id->driver_data;
+
+ gameport_register_port(&vortex->gameport);
+
+ printk(KERN_INFO "gameport: %s at pci%s speed %d kHz\n",
+ dev->name, dev->slot_name, vortex->gameport.speed);
+
+ return 0;
+}
+
+static void __devexit vortex_remove(struct pci_dev *dev)
+{
+ struct vortex *vortex = pci_get_drvdata(dev);
+ gameport_unregister_port(&vortex->gameport);
+ iounmap(vortex->base);
+ kfree(vortex);
+}
+
+static struct pci_device_id vortex_id_table[] __devinitdata =
+{{ 0x12eb, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x11000 },
+ { 0x12eb, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x28800 },
+ { 0 }};
+
+static struct pci_driver vortex_driver = {
+ name: "vortex",
+ id_table: vortex_id_table,
+ probe: vortex_probe,
+ remove: vortex_remove,
+};
+
+int __init vortex_init(void)
+{
+ return pci_module_init(&vortex_driver);
+}
+
+void __exit vortex_exit(void)
+{
+ pci_unregister_driver(&vortex_driver);
+}
+
+module_init(vortex_init);
+module_exit(vortex_exit);
diff --git a/drivers/input/input.c b/drivers/input/input.c
index e80f4a4c8732..93241c7d577e 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -61,8 +61,6 @@ static struct input_dev *input_dev;
static struct input_handler *input_handler;
static struct input_handler *input_table[8];
static devfs_handle_t input_devfs_handle;
-static int input_number;
-static long input_devices[NBITS(INPUT_DEVICES)];
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_bus_input_dir;
@@ -454,17 +452,8 @@ void input_register_device(struct input_dev *dev)
* Add the device.
*/
- if (input_number >= INPUT_DEVICES) {
- printk(KERN_WARNING "input: ran out of input device numbers!\n");
- dev->number = input_number;
- } else {
- dev->number = find_first_zero_bit(input_devices, INPUT_DEVICES);
- set_bit(dev->number, input_devices);
- }
-
dev->next = input_dev;
input_dev = dev;
- input_number++;
/*
* Notify handlers.
@@ -493,7 +482,6 @@ void input_register_device(struct input_dev *dev)
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
-
}
void input_unregister_device(struct input_dev *dev)
@@ -509,7 +497,6 @@ void input_unregister_device(struct input_dev *dev)
if (dev->pm_dev)
pm_unregister(dev->pm_dev);
-
/*
* Kill any pending repeat timers.
*/
@@ -540,7 +527,6 @@ void input_unregister_device(struct input_dev *dev)
*/
input_find_and_remove(struct input_dev, input_dev, dev, next);
- input_number--;
/*
* Notify /proc.
*/
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index e5b845e29468..416aace2a2a1 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -1,5 +1,5 @@
/*
- * $Id: joydev.c,v 1.38 2001/12/27 10:37:41 vojtech Exp $
+ * $Id: joydev.c,v 1.43 2002/04/09 23:59:01 jsimmons Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
* Copyright (c) 1999 Colin Van Dyke
@@ -49,7 +49,7 @@ MODULE_SUPPORTED_DEVICE("input/js");
MODULE_LICENSE("GPL");
#define JOYDEV_MINOR_BASE 0
-#define JOYDEV_MINORS 32
+#define JOYDEV_MINORS 16
#define JOYDEV_BUFFER_SIZE 64
#define MSECS(t) (1000 * ((t) / HZ) + 1000 * ((t) % HZ) / HZ)
@@ -254,6 +254,10 @@ static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *p
while (list->head == list->tail) {
+ if (!joydev->exist) {
+ retval = -ENODEV;
+ break;
+ }
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
@@ -325,6 +329,8 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
struct input_dev *dev = joydev->handle.dev;
int i;
+ if (!joydev->exist) return -ENODEV;
+
switch (cmd) {
case JS_SET_CAL:
diff --git a/drivers/input/joystick/Config.help b/drivers/input/joystick/Config.help
index 1b4187bbb8a3..155f0b83064a 100644
--- a/drivers/input/joystick/Config.help
+++ b/drivers/input/joystick/Config.help
@@ -1,4 +1,4 @@
-CONFIG_JOYSTICK
+CONFIG_INPUT_JOYSTICK
If you have a joystick, 6dof controller, gamepad, steering wheel,
weapon control system or something like that you can say Y here
and the list of supported devices will be displayed. This option
@@ -67,6 +67,15 @@ CONFIG_JOYSTICK_GRIP
The module will be called grip.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+CONFIG_JOYSTICK_GUILLEMOT
+ Say Y here if you have a Guillemot joystick using a digital
+ protocol over the PC gameport.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called guillemot.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
CONFIG_JOYSTICK_INTERACT
Say Y here if you have an InterAct gameport or joystick
communicating digitally over the gameport.
@@ -158,6 +167,15 @@ CONFIG_JOYSTICK_STINGER
The module will be called stinger.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+CONFIG_JOYSTICK_TWIDDLER
+ Say Y here if you have a Handykey Twiddler connected to your
+ computer's serial port and want to use it as a joystick.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called twidjoy.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
CONFIG_JOYSTICK_DB9
Say Y here if you have a Sega Master System gamepad, Sega Genesis
gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
@@ -203,3 +221,12 @@ CONFIG_JOYSTICK_AMIJOY
The module will be called joy-amiga.o. If you want to compile it as
a module, say M here and read <file:Documentation/modules.txt>.
+CONFIG_INPUT_JOYDUMP
+ Say Y here if you want to dump data from your joystick into the system
+ log for debugging purposes. Say N if you are making a production
+ configuration or aren't sure.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called joydump.o. If you want to compile it as
+ a module, say M here and read <file:Documentation/modules.txt>.
diff --git a/drivers/input/joystick/Config.in b/drivers/input/joystick/Config.in
index afd07b89dff3..bb69101b9b9e 100644
--- a/drivers/input/joystick/Config.in
+++ b/drivers/input/joystick/Config.in
@@ -10,6 +10,7 @@ dep_tristate ' Logitech ADI digital joysticks and gamepads' CONFIG_JOYSTICK_ADI
dep_tristate ' Creative Labs Blaster Cobra gamepad' CONFIG_JOYSTICK_COBRA $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' Genius Flight2000 Digital joysticks and gamepads' CONFIG_JOYSTICK_GF2K $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOYSTICK_GRIP $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
+dep_tristate ' Guillemot joysticks and gamepads' CONFIG_JOYSTICK_GUILLEMOT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' InterAct digital joysticks and gamepads' CONFIG_JOYSTICK_INTERACT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' Microsoft SideWinder digital joysticks and gamepads' CONFIG_JOYSTICK_SIDEWINDER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOYSTICK_TMDC $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
@@ -21,6 +22,7 @@ dep_tristate ' LogiCad3d Magellan/SpaceMouse 6dof controllers' CONFIG_JOYSTICK_
dep_tristate ' SpaceTec SpaceOrb/Avenger 6dof controllers' CONFIG_JOYSTICK_SPACEORB $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
dep_tristate ' SpaceTec SpaceBall 6dof controllers' CONFIG_JOYSTICK_SPACEBALL $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
dep_tristate ' Gravis Stinger gamepad' CONFIG_JOYSTICK_STINGER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
+dep_tristate ' Twiddler as a joystick' CONFIG_JOYSTICK_TWIDDLER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
dep_tristate ' Multisystem, Sega Genesis, Saturn joysticks and gamepads' CONFIG_JOYSTICK_DB9 $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT
dep_tristate ' Multisystem, NES, SNES, N64, PSX joysticks and gamepads' CONFIG_JOYSTICK_GAMECON $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT
@@ -29,3 +31,5 @@ dep_tristate ' Multisystem joysticks via TurboGraFX device' CONFIG_JOYSTICK_TUR
if [ "$CONFIG_AMIGA" = "y" ]; then
dep_tristate ' Amiga joysticks' CONFIG_JOYSTICK_AMIJOY $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK
fi
+
+dep_tristate ' Gameport data dumper' CONFIG_INPUT_JOYDUMP $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index c70244633703..16ab6110e13c 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -31,8 +31,9 @@ obj-$(CONFIG_JOYSTICK_DB9) += db9.o
obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o
obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o
obj-$(CONFIG_JOYSTICK_GRIP) += grip.o
-obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
+obj-$(CONFIG_JOYSTICK_GUILLEMOT) += guillemot.o
obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
+obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
@@ -40,8 +41,11 @@ obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
obj-$(CONFIG_JOYSTICK_STINGER) += stinger.o
obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o
obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o
+obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o
obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
+obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/
+
# The global Rules.make.
include $(TOPDIR)/Rules.make
diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c
index 264fe6fc4924..66ed7834a213 100644
--- a/drivers/input/joystick/adi.c
+++ b/drivers/input/joystick/adi.c
@@ -426,10 +426,10 @@ static void adi_init_input(struct adi *adi, struct adi_port *port, int half)
adi->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++)
- set_bit(adi->abs[i], &adi->dev.absbit);
+ set_bit(adi->abs[i], adi->dev.absbit);
for (i = 0; i < adi->buttons; i++)
- set_bit(adi->key[i], &adi->dev.keybit);
+ set_bit(adi->key[i], adi->dev.keybit);
}
static void adi_init_center(struct adi *adi)
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
index 0ce114353b8e..24d75e44d904 100644
--- a/drivers/input/joystick/db9.c
+++ b/drivers/input/joystick/db9.c
@@ -1,5 +1,5 @@
/*
- * $Id: db9.c,v 1.12 2002/01/22 20:27:05 vojtech Exp $
+ * $Id: db9.c,v 1.13 2002/04/07 20:13:37 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
@@ -199,7 +199,7 @@ static void db9_timer(unsigned long private)
data=parport_read_data(port);
input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
- input_report_key(dev, BTN_X, ~data & DB9_FIRE2);
+ input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NOSELECT); /* 2 */
udelay(DB9_GENESIS6_DELAY);
@@ -209,10 +209,10 @@ static void db9_timer(unsigned long private)
udelay(DB9_GENESIS6_DELAY);
data=parport_read_data(port);
- input_report_key(dev, BTN_Y, ~data & DB9_LEFT);
- input_report_key(dev, BTN_Z, ~data & DB9_DOWN);
- input_report_key(dev, BTN_MODE, ~data & DB9_UP);
- input_report_key(dev, BTN_START, ~data & DB9_RIGHT);
+ input_report_key(dev, BTN_X, ~data & DB9_LEFT);
+ input_report_key(dev, BTN_Y, ~data & DB9_DOWN);
+ input_report_key(dev, BTN_Z, ~data & DB9_UP);
+ input_report_key(dev, BTN_MODE, ~data & DB9_RIGHT);
parport_write_control(port, DB9_NORMAL);
udelay(DB9_GENESIS6_DELAY);
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index 77a1036e32d4..23ebebe3adb4 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -1,5 +1,5 @@
/*
- * $Id: gamecon.c,v 1.21 2002/01/22 20:27:27 vojtech Exp $
+ * $Id: gamecon.c,v 1.22 2002/07/01 15:42:25 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
@@ -398,8 +398,8 @@ static void gc_timer(unsigned long private)
case GC_PSX_RUMBLE:
- input_report_key(dev + i, BTN_THUMB, ~data[0] & 0x04);
- input_report_key(dev + i, BTN_THUMB2, ~data[0] & 0x02);
+ input_report_key(dev + i, BTN_THUMBL, ~data[0] & 0x04);
+ input_report_key(dev + i, BTN_THUMBR, ~data[0] & 0x02);
case GC_PSX_NEGCON:
case GC_PSX_ANALOG:
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
new file mode 100644
index 000000000000..53c46728a2bf
--- /dev/null
+++ b/drivers/input/joystick/guillemot.c
@@ -0,0 +1,285 @@
+/*
+ * $Id: guillemot.c,v 1.10 2002/01/22 20:28:12 vojtech Exp $
+ *
+ * Copyright (c) 2001 Vojtech Pavlik
+ */
+
+/*
+ * Guillemot Digital Interface Protocol driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Guillemot Digital joystick driver");
+MODULE_LICENSE("GPL");
+
+#define GUILLEMOT_MAX_START 600 /* 600 us */
+#define GUILLEMOT_MAX_STROBE 60 /* 60 us */
+#define GUILLEMOT_MAX_LENGTH 17 /* 17 bytes */
+#define GUILLEMOT_REFRESH_TIME HZ/50 /* 20 ms */
+
+static short guillemot_abs_pad[] =
+ { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, -1 };
+
+static short guillemot_btn_pad[] =
+ { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_MODE, BTN_SELECT, -1 };
+
+static struct {
+ int x;
+ int y;
+} guillemot_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct guillemot_type {
+ unsigned char id;
+ short *abs;
+ short *btn;
+ int hat;
+ char *name;
+};
+
+struct guillemot {
+ struct gameport *gameport;
+ struct input_dev dev;
+ struct timer_list timer;
+ int used;
+ int bads;
+ int reads;
+ struct guillemot_type *type;
+ unsigned char length;
+ char phys[32];
+};
+
+static struct guillemot_type guillemot_type[] = {
+ { 0x00, guillemot_abs_pad, guillemot_btn_pad, 1, "Guillemot Pad" },
+ { 0 }};
+
+/*
+ * guillemot_read_packet() reads Guillemot joystick data.
+ */
+
+static int guillemot_read_packet(struct gameport *gameport, u8 *data)
+{
+ unsigned long flags;
+ unsigned char u, v;
+ unsigned int t, s;
+ int i;
+
+ for (i = 0; i < GUILLEMOT_MAX_LENGTH; i++)
+ data[i] = 0;
+
+ i = 0;
+ t = gameport_time(gameport, GUILLEMOT_MAX_START);
+ s = gameport_time(gameport, GUILLEMOT_MAX_STROBE);
+
+ __save_flags(flags);
+ __cli();
+ gameport_trigger(gameport);
+ v = gameport_read(gameport);
+
+ while (t > 0 && i < GUILLEMOT_MAX_LENGTH * 8) {
+ t--;
+ u = v; v = gameport_read(gameport);
+ if (v & ~u & 0x10) {
+ data[i >> 3] |= ((v >> 5) & 1) << (i & 7);
+ i++;
+ t = s;
+ }
+ }
+
+ __restore_flags(flags);
+
+ return i;
+}
+
+/*
+ * guillemot_timer() reads and analyzes Guillemot joystick data.
+ */
+
+static void guillemot_timer(unsigned long private)
+{
+ struct guillemot *guillemot = (struct guillemot *) private;
+ struct input_dev *dev = &guillemot->dev;
+ u8 data[GUILLEMOT_MAX_LENGTH];
+ int i;
+
+ guillemot->reads++;
+
+ if (guillemot_read_packet(guillemot->gameport, data) != GUILLEMOT_MAX_LENGTH * 8 ||
+ data[0] != 0x55 || data[16] != 0xaa) {
+ guillemot->bads++;
+ } else {
+
+ for (i = 0; i < 6 && guillemot->type->abs[i] >= 0; i++)
+ input_report_abs(dev, guillemot->type->abs[i], data[i + 5]);
+
+ if (guillemot->type->hat) {
+ input_report_abs(dev, ABS_HAT0X, guillemot_hat_to_axis[data[4] >> 4].x);
+ input_report_abs(dev, ABS_HAT0Y, guillemot_hat_to_axis[data[4] >> 4].y);
+ }
+
+ for (i = 0; i < 16 && guillemot->type->btn[i] >= 0; i++)
+ input_report_key(dev, guillemot->type->btn[i], (data[2 + (i >> 3)] >> (i & 7)) & 1);
+ }
+
+ mod_timer(&guillemot->timer, jiffies + GUILLEMOT_REFRESH_TIME);
+}
+
+/*
+ * guillemot_open() is a callback from the input open routine.
+ */
+
+static int guillemot_open(struct input_dev *dev)
+{
+ struct guillemot *guillemot = dev->private;
+ if (!guillemot->used++)
+ mod_timer(&guillemot->timer, jiffies + GUILLEMOT_REFRESH_TIME);
+ return 0;
+}
+
+/*
+ * guillemot_close() is a callback from the input close routine.
+ */
+
+static void guillemot_close(struct input_dev *dev)
+{
+ struct guillemot *guillemot = dev->private;
+ if (!--guillemot->used)
+ del_timer(&guillemot->timer);
+}
+
+/*
+ * guillemot_connect() probes for Guillemot joysticks.
+ */
+
+static void guillemot_connect(struct gameport *gameport, struct gameport_dev *dev)
+{
+ struct guillemot *guillemot;
+ u8 data[GUILLEMOT_MAX_LENGTH];
+ int i, t;
+
+ if (!(guillemot = kmalloc(sizeof(struct guillemot), GFP_KERNEL)))
+ return;
+ memset(guillemot, 0, sizeof(struct guillemot));
+
+ gameport->private = guillemot;
+
+ guillemot->gameport = gameport;
+ init_timer(&guillemot->timer);
+ guillemot->timer.data = (long) guillemot;
+ guillemot->timer.function = guillemot_timer;
+
+ if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
+ goto fail1;
+
+ i = guillemot_read_packet(gameport, data);
+
+ if (i != GUILLEMOT_MAX_LENGTH * 8 || data[0] != 0x55 || data[16] != 0xaa)
+ goto fail2;
+
+ for (i = 0; guillemot_type[i].name; i++)
+ if (guillemot_type[i].id == data[11])
+ break;
+
+ if (!guillemot_type[i].name) {
+ printk(KERN_WARNING "guillemot.c: Unknown joystick on %s. [ %02x%02x:%04x, ver %d.%02d ]\n",
+ gameport->phys, data[12], data[13], data[11], data[14], data[15]);
+ goto fail2;
+ }
+
+ sprintf(guillemot->phys, "%s/input0", gameport->phys);
+
+ guillemot->type = guillemot_type + i;
+
+ guillemot->dev.private = guillemot;
+ guillemot->dev.open = guillemot_open;
+ guillemot->dev.close = guillemot_close;
+
+ guillemot->dev.name = guillemot_type[i].name;
+ guillemot->dev.phys = guillemot->phys;
+ guillemot->dev.idbus = BUS_GAMEPORT;
+ guillemot->dev.idvendor = GAMEPORT_ID_VENDOR_GUILLEMOT;
+ guillemot->dev.idproduct = guillemot_type[i].id;
+ guillemot->dev.idversion = (int)data[14] << 8 | data[15];
+
+ guillemot->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (i = 0; (t = guillemot->type->abs[i]) >= 0; i++) {
+ set_bit(t, guillemot->dev.absbit);
+ guillemot->dev.absmin[t] = 0;
+ guillemot->dev.absmax[t] = 255;
+ }
+
+ if (guillemot->type->hat)
+ for (i = 0; i < 2; i++) {
+ t = ABS_HAT0X + i;
+ set_bit(t, guillemot->dev.absbit);
+ guillemot->dev.absmin[t] = -1;
+ guillemot->dev.absmax[t] = 1;
+ }
+
+ for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++)
+ set_bit(t, guillemot->dev.keybit);
+
+ input_register_device(&guillemot->dev);
+ printk(KERN_INFO "input: %s ver %d.%02d on %s\n",
+ guillemot->type->name, data[14], data[15], gameport->phys);
+
+ return;
+fail2: gameport_close(gameport);
+fail1: kfree(guillemot);
+}
+
+static void guillemot_disconnect(struct gameport *gameport)
+{
+ struct guillemot *guillemot = gameport->private;
+ printk(KERN_INFO "guillemot.c: Failed %d reads out of %d on %s\n", guillemot->reads, guillemot->bads, guillemot->phys);
+ input_unregister_device(&guillemot->dev);
+ gameport_close(gameport);
+ kfree(guillemot);
+}
+
+static struct gameport_dev guillemot_dev = {
+ connect: guillemot_connect,
+ disconnect: guillemot_disconnect,
+};
+
+int __init guillemot_init(void)
+{
+ gameport_register_device(&guillemot_dev);
+ return 0;
+}
+
+void __exit guillemot_exit(void)
+{
+ gameport_unregister_device(&guillemot_dev);
+}
+
+module_init(guillemot_init);
+module_exit(guillemot_exit);
diff --git a/drivers/input/joystick/iforce.c b/drivers/input/joystick/iforce.c
deleted file mode 100644
index 172c5d99a954..000000000000
--- a/drivers/input/joystick/iforce.c
+++ /dev/null
@@ -1,1224 +0,0 @@
-/*
- * $Id: iforce.c,v 1.56 2001/05/27 14:41:26 jdeneux Exp $
- *
- * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
- * Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
- *
- * USB/RS232 I-Force joysticks and wheels.
- *
- * Sponsored by SuSE
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/usb.h>
-#include <linux/serio.h>
-#include <linux/config.h>
-
-/* FF: This module provides arbitrary resource management routines.
- * I use it to manage the device's memory.
- * Despite the name of this module, I am *not* going to access the ioports.
- */
-#include <linux/ioport.h>
-
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>, Johann Deneux <deneux@ifrance.com>");
-MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
-MODULE_LICENSE("GPL");
-
-#define IFORCE_MAX_LENGTH 16
-
-#if defined(CONFIG_JOYSTICK_IFORCE_232) || defined(CONFIG_JOYSTICK_IFORCE_232_MODULE)
-#define IFORCE_232 1
-#endif
-#if defined(CONFIG_JOYSTICK_IFORCE_USB) || defined(CONFIG_JOYSTICK_IFORCE_USB_MODULE)
-#define IFORCE_USB 2
-#endif
-
-#define FF_EFFECTS_MAX 32
-
-/* Each force feedback effect is made of one core effect, which can be
- * associated to at most to effect modifiers
- */
-#define FF_MOD1_IS_USED 0
-#define FF_MOD2_IS_USED 1
-#define FF_CORE_IS_USED 2
-#define FF_CORE_IS_PLAYED 3
-#define FF_MODCORE_MAX 3
-
-struct iforce_core_effect {
- /* Information about where modifiers are stored in the device's memory */
- struct resource mod1_chunk;
- struct resource mod2_chunk;
- unsigned long flags[NBITS(FF_MODCORE_MAX)];
-};
-
-#define FF_CMD_EFFECT 0x010e
-#define FF_CMD_SHAPE 0x0208
-#define FF_CMD_MAGNITUDE 0x0303
-#define FF_CMD_PERIOD 0x0407
-#define FF_CMD_INTERACT 0x050a
-
-#define FF_CMD_AUTOCENTER 0x4002
-#define FF_CMD_PLAY 0x4103
-#define FF_CMD_ENABLE 0x4201
-#define FF_CMD_GAIN 0x4301
-
-#define FF_CMD_QUERY 0xff01
-
-static signed short btn_joystick[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
- BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, BTN_DEAD, -1 };
-static signed short btn_wheel[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
- BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
-static signed short abs_joystick[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
-static signed short abs_wheel[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 };
-static signed short ff_iforce[] = { FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_FRICTION,
- FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN, FF_AUTOCENTER, -1 };
-
-static struct iforce_device {
- u16 idvendor;
- u16 idproduct;
- char *name;
- signed short *btn;
- signed short *abs;
- signed short *ff;
-} iforce_device[] = {
- { 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce },
- { 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce },
- { 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_joystick, abs_joystick, ff_iforce },
- { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_wheel, abs_wheel, ff_iforce },
- { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce },
- { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce }
-};
-
-struct iforce {
- struct input_dev dev; /* Input device interface */
- struct iforce_device *type;
- char name[64];
- int open;
- int bus;
-
- unsigned char data[IFORCE_MAX_LENGTH];
- unsigned char edata[IFORCE_MAX_LENGTH];
- u16 ecmd;
- u16 expect_packet;
-
-#ifdef IFORCE_232
- struct serio *serio; /* RS232 transfer */
- int idx, pkt, len, id;
- unsigned char csum;
-#endif
-#ifdef IFORCE_USB
- struct usb_device *usbdev; /* USB transfer */
- struct urb *irq, *out, *ctrl;
- struct usb_ctrlrequest dr;
-#endif
- /* Force Feedback */
- wait_queue_head_t wait;
- struct resource device_memory;
- struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
-};
-
-static struct {
- __s32 x;
- __s32 y;
-} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
-
-/* Get hi and low bytes of a 16-bits int */
-#define HI(a) ((unsigned char)((a) >> 8))
-#define LO(a) ((unsigned char)((a) & 0xff))
-
-/* Encode a time value */
-#define TIME_SCALE(a) ((a) == 0xffff ? 0xffff : (a) * 1000 / 256)
-
-static void dump_packet(char *msg, u16 cmd, unsigned char *data)
-{
- int i;
-
- printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
- for (i = 0; i < LO(cmd); i++)
- printk("%02x ", data[i]);
- printk(")\n");
-}
-
-/*
- * Send a packet of bytes to the device
- */
-static void send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
-{
- switch (iforce->bus) {
-
-#ifdef IFORCE_232
- case IFORCE_232: {
-
- int i;
- unsigned char csum = 0x2b ^ HI(cmd) ^ LO(cmd);
-
- serio_write(iforce->serio, 0x2b);
- serio_write(iforce->serio, HI(cmd));
- serio_write(iforce->serio, LO(cmd));
-
- for (i = 0; i < LO(cmd); i++) {
- serio_write(iforce->serio, data[i]);
- csum = csum ^ data[i];
- }
-
- serio_write(iforce->serio, csum);
- return;
- }
-#endif
-#ifdef IFORCE_USB
- case IFORCE_USB: {
-
- DECLARE_WAITQUEUE(wait, current);
- int timeout = HZ; /* 1 second */
-
- memcpy(iforce->out->transfer_buffer + 1, data, LO(cmd));
- ((char*)iforce->out->transfer_buffer)[0] = HI(cmd);
- iforce->out->transfer_buffer_length = LO(cmd) + 2;
- iforce->out->dev = iforce->usbdev;
-
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&iforce->wait, &wait);
-
- if (usb_submit_urb(iforce->out, GFP_ATOMIC)) {
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&iforce->wait, &wait);
- return;
- }
-
- while (timeout && iforce->out->status == -EINPROGRESS)
- timeout = schedule_timeout(timeout);
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&iforce->wait, &wait);
-
- if (!timeout)
- usb_unlink_urb(iforce->out);
-
- return;
- }
-#endif
- }
-}
-
-static void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
-{
- struct input_dev *dev = &iforce->dev;
- int i;
-
-#ifdef IFORCE_232
- if (HI(iforce->expect_packet) == HI(cmd)) {
- iforce->expect_packet = 0;
- iforce->ecmd = cmd;
- memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
- if (waitqueue_active(&iforce->wait))
- wake_up(&iforce->wait);
- }
-#endif
-
- if (!iforce->type)
- return;
-
- switch (HI(cmd)) {
-
- case 0x01: /* joystick position data */
- case 0x03: /* wheel position data */
-
- if (HI(cmd) == 1) {
- input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
- input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
- input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
- } else {
- input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
- input_report_abs(dev, ABS_GAS, 255 - data[2]);
- input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
- }
-
- input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
- input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
-
- for (i = 0; iforce->type->btn[i] >= 0; i++)
- input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
-
- break;
-
- case 0x02: /* status report */
-
- input_report_key(dev, BTN_DEAD, data[0] & 0x02);
- break;
- }
-}
-
-static int get_id_packet(struct iforce *iforce, char *packet)
-{
- DECLARE_WAITQUEUE(wait, current);
- int timeout = HZ; /* 1 second */
-
- switch (iforce->bus) {
-
-#ifdef IFORCE_USB
- case IFORCE_USB:
-
- iforce->dr.bRequest = packet[0];
- iforce->ctrl->dev = iforce->usbdev;
-
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&iforce->wait, &wait);
-
- if (usb_submit_urb(iforce->ctrl, GFP_ATOMIC)) {
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&iforce->wait, &wait);
- return -1;
- }
-
- while (timeout && iforce->ctrl->status == -EINPROGRESS)
- timeout = schedule_timeout(timeout);
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&iforce->wait, &wait);
-
- if (!timeout) {
- usb_unlink_urb(iforce->ctrl);
- return -1;
- }
-
- break;
-#endif
-#ifdef IFORCE_232
- case IFORCE_232:
-
- iforce->expect_packet = FF_CMD_QUERY;
- send_packet(iforce, FF_CMD_QUERY, packet);
-
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&iforce->wait, &wait);
-
- while (timeout && iforce->expect_packet)
- timeout = schedule_timeout(timeout);
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&iforce->wait, &wait);
-
- if (!timeout) {
- iforce->expect_packet = 0;
- return -1;
- }
-
- break;
-#endif
- }
-
- return -(iforce->edata[0] != packet[0]);
-}
-
-static int iforce_open(struct input_dev *dev)
-{
- struct iforce *iforce = dev->private;
-
- switch (iforce->bus) {
-#ifdef IFORCE_USB
- case IFORCE_USB:
- if (iforce->open++)
- break;
- iforce->irq->dev = iforce->usbdev;
- if (usb_submit_urb(iforce->irq, GFP_KERNEL))
- return -EIO;
- break;
-#endif
- }
- return 0;
-}
-
-static void iforce_close(struct input_dev *dev)
-{
- struct iforce *iforce = dev->private;
-
- switch (iforce->bus) {
-#ifdef IFORCE_USB
- case IFORCE_USB:
- if (!--iforce->open)
- usb_unlink_urb(iforce->irq);
- break;
-#endif
- }
-}
-
-/*
- * Start or stop playing an effect
- */
-
-static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
-{
- struct iforce* iforce = (struct iforce*)(dev->private);
- unsigned char data[3];
-
- printk(KERN_DEBUG "iforce.c: input_event(type = %d, code = %d, value = %d)\n", type, code, value);
-
- if (type != EV_FF)
- return -1;
-
- switch (code) {
-
- case FF_GAIN:
-
- data[0] = value >> 9;
- send_packet(iforce, FF_CMD_GAIN, data);
-
- return 0;
-
- case FF_AUTOCENTER:
-
- data[0] = 0x03;
- data[1] = value >> 9;
- send_packet(iforce, FF_CMD_AUTOCENTER, data);
-
- data[0] = 0x04;
- data[1] = 0x01;
- send_packet(iforce, FF_CMD_AUTOCENTER, data);
-
- return 0;
-
- default: /* Play an effect */
-
- if (code >= iforce->dev.ff_effects_max)
- return -1;
-
- data[0] = LO(code);
- data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
- data[2] = LO(value);
- send_packet(iforce, FF_CMD_PLAY, data);
-
- return 0;
- }
-
- return -1;
-}
-
-/*
- * Set the magnitude of a constant force effect
- * Return error code
- *
- * Note: caller must ensure exclusive access to device
- */
-
-static int make_magnitude_modifier(struct iforce* iforce,
- struct resource* mod_chunk, __s16 level)
-{
- unsigned char data[3];
-
- if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
- iforce->device_memory.start, iforce->device_memory.end, 2L,
- NULL, NULL)) {
- return -ENOMEM;
- }
-
- data[0] = LO(mod_chunk->start);
- data[1] = HI(mod_chunk->start);
- data[2] = HI(level);
-
- send_packet(iforce, FF_CMD_MAGNITUDE, data);
-
- return 0;
-}
-
-/*
- * Upload the component of an effect dealing with the period, phase and magnitude
- */
-
-static int make_period_modifier(struct iforce* iforce, struct resource* mod_chunk,
- __s16 magnitude, __s16 offset, u16 period, u16 phase)
-{
- unsigned char data[7];
-
- period = TIME_SCALE(period);
-
- if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
- iforce->device_memory.start, iforce->device_memory.end, 2L,
- NULL, NULL)) {
- return -ENOMEM;
- }
-
- data[0] = LO(mod_chunk->start);
- data[1] = HI(mod_chunk->start);
-
- data[2] = HI(magnitude);
- data[3] = HI(offset);
- data[4] = HI(phase);
-
- data[5] = LO(period);
- data[6] = HI(period);
-
- send_packet(iforce, FF_CMD_PERIOD, data);
-
- return 0;
-}
-
-/*
- * Uploads the part of an effect setting the shape of the force
- */
-
-static int make_shape_modifier(struct iforce* iforce, struct resource* mod_chunk,
- u16 attack_duration, __s16 initial_level,
- u16 fade_duration, __s16 final_level)
-{
- unsigned char data[8];
-
- attack_duration = TIME_SCALE(attack_duration);
- fade_duration = TIME_SCALE(fade_duration);
-
- if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
- iforce->device_memory.start, iforce->device_memory.end, 2L,
- NULL, NULL)) {
- return -ENOMEM;
- }
-
- data[0] = LO(mod_chunk->start);
- data[1] = HI(mod_chunk->start);
-
- data[2] = LO(attack_duration);
- data[3] = HI(attack_duration);
- data[4] = HI(initial_level);
-
- data[5] = LO(fade_duration);
- data[6] = HI(fade_duration);
- data[7] = HI(final_level);
-
- send_packet(iforce, FF_CMD_SHAPE, data);
-
- return 0;
-}
-
-/*
- * Component of spring, friction, inertia... effects
- */
-
-static int make_interactive_modifier(struct iforce* iforce,
- struct resource* mod_chunk,
- __s16 rsat, __s16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
-{
- unsigned char data[10];
-
- if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
- iforce->device_memory.start, iforce->device_memory.end, 2L,
- NULL, NULL)) {
- return -ENOMEM;
- }
-
- data[0] = LO(mod_chunk->start);
- data[1] = HI(mod_chunk->start);
-
- data[2] = HI(rk);
- data[3] = HI(lk);
-
- data[4] = LO(center);
- data[5] = HI(center);
-
- data[6] = LO(db);
- data[7] = HI(db);
-
- data[8] = HI(rsat);
- data[9] = HI(lsat);
-
- send_packet(iforce, FF_CMD_INTERACT, data);
-
- return 0;
-}
-
-static unsigned char find_button(struct iforce *iforce, signed short button)
-{
- int i;
- for (i = 1; iforce->type->btn[i] >= 0; i++)
- if (iforce->type->btn[i] == button)
- return i + 1;
- return 0;
-}
-
-/*
- * Send the part common to all effects to the device
- */
-
-static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
- u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
- u16 interval, u16 direction)
-{
- unsigned char data[14];
-
- duration = TIME_SCALE(duration);
- delay = TIME_SCALE(delay);
- interval = TIME_SCALE(interval);
-
- data[0] = LO(id);
- data[1] = effect_type;
- data[2] = LO(axes) | find_button(iforce, button);
-
- data[3] = LO(duration);
- data[4] = HI(duration);
-
- data[5] = HI(direction);
-
- data[6] = LO(interval);
- data[7] = HI(interval);
-
- data[8] = LO(mod_id1);
- data[9] = HI(mod_id1);
- data[10] = LO(mod_id2);
- data[11] = HI(mod_id2);
-
- data[12] = LO(delay);
- data[13] = HI(delay);
-
- send_packet(iforce, FF_CMD_EFFECT, data);
-
- return 0;
-}
-
-/*
- * Upload a periodic effect to the device
- */
-
-static int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect)
-{
- u8 wave_code;
- int core_id = effect->id;
- struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
- struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
- struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
- int err = 0;
-
- err = make_period_modifier(iforce, mod1_chunk,
- effect->u.periodic.magnitude, effect->u.periodic.offset,
- effect->u.periodic.period, effect->u.periodic.phase);
- if (err) return err;
- set_bit(FF_MOD1_IS_USED, core_effect->flags);
-
- err = make_shape_modifier(iforce, mod2_chunk,
- effect->u.periodic.shape.attack_length,
- effect->u.periodic.shape.attack_level,
- effect->u.periodic.shape.fade_length,
- effect->u.periodic.shape.fade_level);
- if (err) return err;
- set_bit(FF_MOD2_IS_USED, core_effect->flags);
-
- switch (effect->u.periodic.waveform) {
- case FF_SQUARE: wave_code = 0x20; break;
- case FF_TRIANGLE: wave_code = 0x21; break;
- case FF_SINE: wave_code = 0x22; break;
- case FF_SAW_UP: wave_code = 0x23; break;
- case FF_SAW_DOWN: wave_code = 0x24; break;
- default: wave_code = 0x20; break;
- }
-
- err = make_core(iforce, effect->id,
- mod1_chunk->start,
- mod2_chunk->start,
- wave_code,
- 0x20,
- effect->replay.length,
- effect->replay.delay,
- effect->trigger.button,
- effect->trigger.interval,
- effect->direction);
-
- return err;
-}
-
-/*
- * Upload a constant force effect
- */
-static int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect)
-{
- int core_id = effect->id;
- struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
- struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
- struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
- int err = 0;
-
- printk(KERN_DEBUG "iforce.c: make constant effect\n");
-
- err = make_magnitude_modifier(iforce, mod1_chunk, effect->u.constant.level);
- if (err) return err;
- set_bit(FF_MOD1_IS_USED, core_effect->flags);
-
- err = make_shape_modifier(iforce, mod2_chunk,
- effect->u.constant.shape.attack_length,
- effect->u.constant.shape.attack_level,
- effect->u.constant.shape.fade_length,
- effect->u.constant.shape.fade_level);
- if (err) return err;
- set_bit(FF_MOD2_IS_USED, core_effect->flags);
-
- err = make_core(iforce, effect->id,
- mod1_chunk->start,
- mod2_chunk->start,
- 0x00,
- 0x20,
- effect->replay.length,
- effect->replay.delay,
- effect->trigger.button,
- effect->trigger.interval,
- effect->direction);
-
- return err;
-}
-
-/*
- * Upload an interactive effect. Those are for example friction, inertia, springs...
- */
-static int iforce_upload_interactive(struct iforce* iforce, struct ff_effect* effect)
-{
- int core_id = effect->id;
- struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
- struct resource* mod_chunk = &(core_effect->mod1_chunk);
- u8 type, axes;
- u16 mod1, mod2, direction;
- int err = 0;
-
- printk(KERN_DEBUG "iforce.c: make interactive effect\n");
-
- switch (effect->type) {
- case FF_SPRING: type = 0x40; break;
- case FF_FRICTION: type = 0x41; break;
- default: return -1;
- }
-
- err = make_interactive_modifier(iforce, mod_chunk,
- effect->u.interactive.right_saturation,
- effect->u.interactive.left_saturation,
- effect->u.interactive.right_coeff,
- effect->u.interactive.left_coeff,
- effect->u.interactive.deadband,
- effect->u.interactive.center);
- if (err) return err;
- set_bit(FF_MOD1_IS_USED, core_effect->flags);
-
- switch ((test_bit(ABS_X, &effect->u.interactive.axis) ||
- test_bit(ABS_WHEEL, &effect->u.interactive.axis)) |
- (!!test_bit(ABS_Y, &effect->u.interactive.axis) << 1)) {
-
- case 0: /* Only one axis, choose orientation */
- mod1 = mod_chunk->start;
- mod2 = 0xffff;
- direction = effect->direction;
- axes = 0x20;
- break;
-
- case 1: /* Only X axis */
- mod1 = mod_chunk->start;
- mod2 = 0xffff;
- direction = 0x5a00;
- axes = 0x40;
- break;
-
- case 2: /* Only Y axis */
- mod1 = 0xffff;
- mod2 = mod_chunk->start;
- direction = 0xb400;
- axes = 0x80;
- break;
-
- case 3: /* Both X and Y axes */
- /* TODO: same setting for both axes is not mandatory */
- mod1 = mod_chunk->start;
- mod2 = mod_chunk->start;
- direction = 0x6000;
- axes = 0xc0;
- break;
-
- default:
- return -1;
- }
-
- err = make_core(iforce, effect->id,
- mod1, mod2,
- type, axes,
- effect->replay.length, effect->replay.delay,
- effect->trigger.button, effect->trigger.interval,
- direction);
-
- return err;
-}
-
-/*
- * Function called when an ioctl is performed on the event dev entry.
- * It uploads an effect to the device
- */
-static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
-{
- struct iforce* iforce = (struct iforce*)(dev->private);
- int id;
-
- printk(KERN_DEBUG "iforce.c: upload effect\n");
-
-/*
- * Get a free id
- */
-
- for (id=0; id < FF_EFFECTS_MAX; ++id)
- if (!test_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break;
-
- if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max)
- return -ENOMEM;
-
- effect->id = id;
- set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags);
-
-/*
- * Upload the effect
- */
-
- switch (effect->type) {
-
- case FF_PERIODIC:
- return iforce_upload_periodic(iforce, effect);
-
- case FF_CONSTANT:
- return iforce_upload_constant(iforce, effect);
-
- case FF_SPRING:
- case FF_FRICTION:
- return iforce_upload_interactive(iforce, effect);
-
- default:
- return -1;
- }
-}
-
-/*
- * Erases an effect: it frees the effect id and mark as unused the memory
- * allocated for the parameters
- */
-static int iforce_erase_effect(struct input_dev *dev, int effect_id)
-{
- struct iforce* iforce = (struct iforce*)(dev->private);
- int err = 0;
- struct iforce_core_effect* core_effect;
-
- printk(KERN_DEBUG "iforce.c: erase effect %d\n", effect_id);
-
- if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
- return -EINVAL;
-
- core_effect = iforce->core_effects + effect_id;
-
- if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
- err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk));
-
- if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
- err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk));
-
- /*TODO: remember to change that if more FF_MOD* bits are added */
- core_effect->flags[0] = 0;
-
- return err;
-}
-static int iforce_init_device(struct iforce *iforce)
-{
- unsigned char c[] = "CEOV";
- int i;
-
- init_waitqueue_head(&iforce->wait);
- iforce->dev.ff_effects_max = 10;
-
-/*
- * Input device fields.
- */
-
- iforce->dev.idbus = BUS_USB;
- iforce->dev.private = iforce;
- iforce->dev.name = iforce->name;
- iforce->dev.open = iforce_open;
- iforce->dev.close = iforce_close;
- iforce->dev.event = iforce_input_event;
- iforce->dev.upload_effect = iforce_upload_effect;
- iforce->dev.erase_effect = iforce_erase_effect;
-
-/*
- * On-device memory allocation.
- */
-
- iforce->device_memory.name = "I-Force device effect memory";
- iforce->device_memory.start = 0;
- iforce->device_memory.end = 200;
- iforce->device_memory.flags = IORESOURCE_MEM;
- iforce->device_memory.parent = NULL;
- iforce->device_memory.child = NULL;
- iforce->device_memory.sibling = NULL;
-
-/*
- * Wait until device ready - until it sends its first response.
- */
-
- for (i = 0; i < 20; i++)
- if (!get_id_packet(iforce, "O"))
- break;
-
- if (i == 20) { /* 5 seconds */
- printk(KERN_ERR "iforce.c: Timeout waiting for response from device.\n");
- iforce_close(&iforce->dev);
- return -1;
- }
-
-/*
- * Get device info.
- */
-
- if (!get_id_packet(iforce, "M"))
- iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1];
- if (!get_id_packet(iforce, "P"))
- iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1];
- if (!get_id_packet(iforce, "B"))
- iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
- if (!get_id_packet(iforce, "N"))
- iforce->dev.ff_effects_max = iforce->edata[1];
-
-/*
- * Display additional info.
- */
-
- for (i = 0; c[i]; i++)
- if (!get_id_packet(iforce, c + i))
- dump_packet("info", iforce->ecmd, iforce->edata);
-
-/*
- * Disable spring, enable force feedback.
- * FIXME: We should use iforce_set_autocenter() et al here.
- */
-
- send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000");
- send_packet(iforce, FF_CMD_ENABLE, "\004");
-
-/*
- * Find appropriate device entry
- */
-
- for (i = 0; iforce_device[i].idvendor; i++)
- if (iforce_device[i].idvendor == iforce->dev.idvendor &&
- iforce_device[i].idproduct == iforce->dev.idproduct)
- break;
-
- iforce->type = iforce_device + i;
-
- sprintf(iforce->name, iforce->type->name,
- iforce->dev.idproduct, iforce->dev.idvendor);
-
-/*
- * Set input device bitfields and ranges.
- */
-
- iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF);
-
- for (i = 0; iforce->type->btn[i] >= 0; i++) {
- signed short t = iforce->type->btn[i];
- set_bit(t, iforce->dev.keybit);
- if (t != BTN_DEAD)
- set_bit(FF_BTN(t), iforce->dev.ffbit);
- }
-
- for (i = 0; iforce->type->abs[i] >= 0; i++) {
-
- signed short t = iforce->type->abs[i];
- set_bit(t, iforce->dev.absbit);
-
- switch (t) {
-
- case ABS_X:
- case ABS_Y:
- case ABS_WHEEL:
-
- iforce->dev.absmax[t] = 1920;
- iforce->dev.absmin[t] = -1920;
- iforce->dev.absflat[t] = 128;
- iforce->dev.absfuzz[t] = 16;
-
- set_bit(FF_ABS(t), iforce->dev.ffbit);
- break;
-
- case ABS_THROTTLE:
- case ABS_GAS:
- case ABS_BRAKE:
-
- iforce->dev.absmax[t] = 255;
- iforce->dev.absmin[t] = 0;
- break;
-
- case ABS_HAT0X:
- case ABS_HAT0Y:
- iforce->dev.absmax[t] = 1;
- iforce->dev.absmin[t] = -1;
- break;
- }
- }
-
- for (i = 0; iforce->type->ff[i] >= 0; i++)
- set_bit(iforce->type->ff[i], iforce->dev.ffbit);
-
-/*
- * Register input device.
- */
-
- input_register_device(&iforce->dev);
-
- return 0;
-}
-
-#ifdef IFORCE_USB
-
-static void iforce_usb_irq(struct urb *urb)
-{
- struct iforce *iforce = urb->context;
- if (urb->status) return;
- iforce_process_packet(iforce,
- (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
-}
-
-static void iforce_usb_out(struct urb *urb)
-{
- struct iforce *iforce = urb->context;
- if (urb->status) return;
- if (waitqueue_active(&iforce->wait))
- wake_up(&iforce->wait);
-}
-
-static void iforce_usb_ctrl(struct urb *urb)
-{
- struct iforce *iforce = urb->context;
- if (urb->status) return;
- iforce->ecmd = 0xff00 | urb->actual_length;
- if (waitqueue_active(&iforce->wait))
- wake_up(&iforce->wait);
-}
-
-static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum,
- const struct usb_device_id *id)
-{
- struct usb_endpoint_descriptor *epirq, *epout;
- struct iforce *iforce;
-
- epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
- epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1;
-
- if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) return NULL;
- memset(iforce, 0, sizeof(struct iforce));
-
- iforce->irq = usb_alloc_urb(0, GFP_KERNEL);
- if (!iforce->irq) {
- kfree(iforce);
- return NULL;
- }
- iforce->out = usb_alloc_urb(0, GFP_KERNEL);
- if (!iforce->out) {
- usb_free_urb(iforce->irq);
- kfree(iforce);
- return NULL;
- }
- iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL);
- if (!iforce->ctrl) {
- usb_free_urb(iforce->out);
- usb_free_urb(iforce->irq);
- kfree(iforce);
- return NULL;
- }
-
- iforce->bus = IFORCE_USB;
- iforce->usbdev = dev;
-
- iforce->dr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
- iforce->dr.wIndex = 0;
- iforce->dr.wLength = 16;
-
- FILL_INT_URB(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
- iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
-
- FILL_BULK_URB(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress),
- iforce + 1, 32, iforce_usb_out, iforce);
-
- FILL_CONTROL_URB(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
- (void*) &iforce->dr, iforce->edata, 16, iforce_usb_ctrl, iforce);
-
- if (iforce_init_device(iforce)) {
- usb_free_urb(iforce->ctrl);
- usb_free_urb(iforce->out);
- usb_free_urb(iforce->irq);
- kfree(iforce);
- return NULL;
- }
-
- printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on usb%d:%d.%d\n",
- iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max,
- iforce->device_memory.end, dev->bus->busnum, dev->devnum, ifnum);
-
- return iforce;
-}
-
-static void iforce_usb_disconnect(struct usb_device *dev, void *ptr)
-{
- struct iforce *iforce = ptr;
- usb_unlink_urb(iforce->irq);
- input_unregister_device(&iforce->dev);
- usb_free_urb(iforce->ctrl);
- usb_free_urb(iforce->out);
- usb_free_urb(iforce->irq);
- kfree(iforce);
-}
-
-static struct usb_device_id iforce_usb_ids [] = {
- { USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */
- { USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */
- { USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */
- { USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */
- { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
-
-static struct usb_driver iforce_usb_driver = {
- name: "iforce",
- probe: iforce_usb_probe,
- disconnect: iforce_usb_disconnect,
- id_table: iforce_usb_ids,
-};
-
-#endif
-
-#ifdef IFORCE_232
-
-static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags)
-{
- struct iforce* iforce = serio->private;
-
- if (!iforce->pkt) {
- if (data != 0x2b) {
- return;
- }
- iforce->pkt = 1;
- return;
- }
-
- if (!iforce->id) {
- if (data > 3 && data != 0xff) {
- iforce->pkt = 0;
- return;
- }
- iforce->id = data;
- return;
- }
-
- if (!iforce->len) {
- if (data > IFORCE_MAX_LENGTH) {
- iforce->pkt = 0;
- iforce->id = 0;
- return;
- }
- iforce->len = data;
- return;
- }
-
- if (iforce->idx < iforce->len) {
- iforce->csum += iforce->data[iforce->idx++] = data;
- return;
- }
-
- if (iforce->idx == iforce->len) {
- iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data);
- iforce->pkt = 0;
- iforce->id = 0;
- iforce->len = 0;
- iforce->idx = 0;
- iforce->csum = 0;
- }
-}
-
-static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev)
-{
- struct iforce *iforce;
- if (serio->type != (SERIO_RS232 | SERIO_IFORCE))
- return;
-
- if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return;
- memset(iforce, 0, sizeof(struct iforce));
-
- iforce->bus = IFORCE_232;
- iforce->serio = serio;
- serio->private = iforce;
-
- if (serio_open(serio, dev)) {
- kfree(iforce);
- return;
- }
-
- if (iforce_init_device(iforce)) {
- serio_close(serio);
- kfree(iforce);
- return;
- }
-
- printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on serio%d\n",
- iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max,
- iforce->device_memory.end, serio->number);
-}
-
-static void iforce_serio_disconnect(struct serio *serio)
-{
- struct iforce* iforce = serio->private;
-
- input_unregister_device(&iforce->dev);
- serio_close(serio);
- kfree(iforce);
-}
-
-static struct serio_dev iforce_serio_dev = {
- interrupt: iforce_serio_irq,
- connect: iforce_serio_connect,
- disconnect: iforce_serio_disconnect,
-};
-
-#endif
-
-static int __init iforce_init(void)
-{
-#ifdef IFORCE_USB
- usb_register(&iforce_usb_driver);
-#endif
-#ifdef IFORCE_232
- serio_register_device(&iforce_serio_dev);
-#endif
- return 0;
-}
-
-static void __exit iforce_exit(void)
-{
-#ifdef IFORCE_USB
- usb_deregister(&iforce_usb_driver);
-#endif
-#ifdef IFORCE_232
- serio_unregister_device(&iforce_serio_dev);
-#endif
-}
-
-module_init(iforce_init);
-module_exit(iforce_exit);
diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile
new file mode 100644
index 000000000000..4eb258e273cd
--- /dev/null
+++ b/drivers/input/joystick/iforce/Makefile
@@ -0,0 +1,42 @@
+#
+# Makefile for the I-Force driver
+#
+
+# I-Force may need both USB and RS-232
+
+CONFIG_JOYSTICK_IFORCE := n
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
+ ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
+ CONFIG_JOYSTICK_IFORCE := y
+ endif
+endif
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_232),m)
+ CONFIG_JOYSTICK_IFORCE := m
+endif
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),m)
+ CONFIG_JOYSTICK_IFORCE := m
+endif
+
+obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
+
+# iforce.o is a multipart module.
+
+IFORCEOBJS = iforce-ff.o iforce-main.o iforce-packets.o
+
+ifneq ($(CONFIG_JOYSTICK_IFORCE_232),)
+ IFORCEOBJS += iforce-serio.o
+endif
+
+ifneq ($(CONFIG_JOYSTICK_IFORCE_USB),)
+ IFORCEOBJS += iforce-usb.o
+endif
+
+iforce.o: $(IFORCEOBJS)
+ $(LD) -i $(IFORCEOBJS) -o $@
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
new file mode 100644
index 000000000000..267f92f0aa2f
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-ff.c
@@ -0,0 +1,543 @@
+/*
+ * $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ * USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+/*
+ * Set the magnitude of a constant force effect
+ * Return error code
+ *
+ * Note: caller must ensure exclusive access to device
+ */
+
+static int make_magnitude_modifier(struct iforce* iforce,
+ struct resource* mod_chunk, int no_alloc, __s16 level)
+{
+ unsigned char data[3];
+
+ if (!no_alloc) {
+ down(&iforce->mem_mutex);
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ up(&iforce->mem_mutex);
+ return -ENOMEM;
+ }
+ up(&iforce->mem_mutex);
+ }
+
+ data[0] = LO(mod_chunk->start);
+ data[1] = HI(mod_chunk->start);
+ data[2] = HIFIX80(level);
+
+ iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);
+
+ iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data);
+ return 0;
+}
+
+/*
+ * Upload the component of an effect dealing with the period, phase and magnitude
+ */
+
+static int make_period_modifier(struct iforce* iforce,
+ struct resource* mod_chunk, int no_alloc,
+ __s16 magnitude, __s16 offset, u16 period, u16 phase)
+{
+ unsigned char data[7];
+
+ period = TIME_SCALE(period);
+
+ if (!no_alloc) {
+ down(&iforce->mem_mutex);
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ up(&iforce->mem_mutex);
+ return -ENOMEM;
+ }
+ up(&iforce->mem_mutex);
+ }
+
+ data[0] = LO(mod_chunk->start);
+ data[1] = HI(mod_chunk->start);
+
+ data[2] = HIFIX80(magnitude);
+ data[3] = HIFIX80(offset);
+ data[4] = HI(phase);
+
+ data[5] = LO(period);
+ data[6] = HI(period);
+
+ iforce_send_packet(iforce, FF_CMD_PERIOD, data);
+
+ return 0;
+}
+
+/*
+ * Uploads the part of an effect setting the envelope of the force
+ */
+
+static int make_envelope_modifier(struct iforce* iforce,
+ struct resource* mod_chunk, int no_alloc,
+ u16 attack_duration, __s16 initial_level,
+ u16 fade_duration, __s16 final_level)
+{
+ unsigned char data[8];
+
+ attack_duration = TIME_SCALE(attack_duration);
+ fade_duration = TIME_SCALE(fade_duration);
+
+ if (!no_alloc) {
+ down(&iforce->mem_mutex);
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ up(&iforce->mem_mutex);
+ return -ENOMEM;
+ }
+ up(&iforce->mem_mutex);
+ }
+
+ data[0] = LO(mod_chunk->start);
+ data[1] = HI(mod_chunk->start);
+
+ data[2] = LO(attack_duration);
+ data[3] = HI(attack_duration);
+ data[4] = HI(initial_level);
+
+ data[5] = LO(fade_duration);
+ data[6] = HI(fade_duration);
+ data[7] = HI(final_level);
+
+ iforce_send_packet(iforce, FF_CMD_ENVELOPE, data);
+
+ return 0;
+}
+
+/*
+ * Component of spring, friction, inertia... effects
+ */
+
+static int make_condition_modifier(struct iforce* iforce,
+ struct resource* mod_chunk, int no_alloc,
+ __u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
+{
+ unsigned char data[10];
+
+ if (!no_alloc) {
+ down(&iforce->mem_mutex);
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ up(&iforce->mem_mutex);
+ return -ENOMEM;
+ }
+ up(&iforce->mem_mutex);
+ }
+
+ data[0] = LO(mod_chunk->start);
+ data[1] = HI(mod_chunk->start);
+
+ data[2] = (100*rk)>>15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
+ data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */
+
+ center = (500*center)>>15;
+ data[4] = LO(center);
+ data[5] = HI(center);
+
+ db = (1000*db)>>16;
+ data[6] = LO(db);
+ data[7] = HI(db);
+
+ data[8] = (100*rsat)>>16;
+ data[9] = (100*lsat)>>16;
+
+ iforce_send_packet(iforce, FF_CMD_CONDITION, data);
+ iforce_dump_packet("condition", FF_CMD_CONDITION, data);
+
+ return 0;
+}
+
+static unsigned char find_button(struct iforce *iforce, signed short button)
+{
+ int i;
+ for (i = 1; iforce->type->btn[i] >= 0; i++)
+ if (iforce->type->btn[i] == button)
+ return i + 1;
+ return 0;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an condition
+ * parameter packet
+ */
+static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new)
+{
+ int id = new->id;
+ struct ff_effect* old = &iforce->core_effects[id].effect;
+ int ret=0;
+ int i;
+
+ if (new->type != FF_SPRING && new->type != FF_FRICTION) {
+ printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n");
+ return FALSE;
+ }
+
+ for(i=0; i<2; i++) {
+ ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation
+ || old->u.condition[i].left_saturation != new->u.condition[i].left_saturation
+ || old->u.condition[i].right_coeff != new->u.condition[i].right_coeff
+ || old->u.condition[i].left_coeff != new->u.condition[i].left_coeff
+ || old->u.condition[i].deadband != new->u.condition[i].deadband
+ || old->u.condition[i].center != new->u.condition[i].center;
+ }
+ return ret;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send a magnitude
+ * parameter packet
+ */
+static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect)
+{
+ int id = effect->id;
+ struct ff_effect* old = &iforce->core_effects[id].effect;
+
+ if (effect->type != FF_CONSTANT) {
+ printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
+ return FALSE;
+ }
+
+ return (old->u.constant.level != effect->u.constant.level);
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an envelope
+ * parameter packet
+ */
+static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effect)
+{
+ int id = effect->id;
+ struct ff_effect* old = &iforce->core_effects[id].effect;
+
+ switch (effect->type) {
+ case FF_CONSTANT:
+ if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length
+ || old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level
+ || old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length
+ || old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)
+ return TRUE;
+ break;
+
+ case FF_PERIODIC:
+ if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length
+ || old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level
+ || old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length
+ || old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)
+ return TRUE;
+ break;
+
+ default:
+ printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
+ }
+
+ return FALSE;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send a periodic
+ * parameter effect
+ */
+static int need_period_modifier(struct iforce* iforce, struct ff_effect* new)
+{
+ int id = new->id;
+ struct ff_effect* old = &iforce->core_effects[id].effect;
+
+ if (new->type != FF_PERIODIC) {
+ printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n");
+ return FALSE;
+ }
+
+ return (old->u.periodic.period != new->u.periodic.period
+ || old->u.periodic.magnitude != new->u.periodic.magnitude
+ || old->u.periodic.offset != new->u.periodic.offset
+ || old->u.periodic.phase != new->u.periodic.phase);
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an effect
+ * packet
+ */
+static int need_core(struct iforce* iforce, struct ff_effect* new)
+{
+ int id = new->id;
+ struct ff_effect* old = &iforce->core_effects[id].effect;
+
+ if (old->direction != new->direction
+ || old->trigger.button != new->trigger.button
+ || old->trigger.interval != new->trigger.interval
+ || old->replay.length != new->replay.length
+ || old->replay.delay != new->replay.delay)
+ return TRUE;
+
+ return FALSE;
+}
+/*
+ * Send the part common to all effects to the device
+ */
+static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
+ u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
+ u16 interval, u16 direction)
+{
+ unsigned char data[14];
+
+ duration = TIME_SCALE(duration);
+ delay = TIME_SCALE(delay);
+ interval = TIME_SCALE(interval);
+
+ data[0] = LO(id);
+ data[1] = effect_type;
+ data[2] = LO(axes) | find_button(iforce, button);
+
+ data[3] = LO(duration);
+ data[4] = HI(duration);
+
+ data[5] = HI(direction);
+
+ data[6] = LO(interval);
+ data[7] = HI(interval);
+
+ data[8] = LO(mod_id1);
+ data[9] = HI(mod_id1);
+ data[10] = LO(mod_id2);
+ data[11] = HI(mod_id2);
+
+ data[12] = LO(delay);
+ data[13] = HI(delay);
+
+ /* Stop effect */
+/* iforce_control_playback(iforce, id, 0);*/
+
+ iforce_send_packet(iforce, FF_CMD_EFFECT, data);
+
+ /* If needed, restart effect */
+ if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {
+ /* BUG: perhaps we should replay n times, instead of 1. But we do not know n */
+ iforce_control_playback(iforce, id, 1);
+ }
+
+ return 0;
+}
+
+/*
+ * Upload a periodic effect to the device
+ * See also iforce_upload_constant.
+ */
+int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+ u8 wave_code;
+ int core_id = effect->id;
+ struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+ struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+ struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+ int param1_err = 1;
+ int param2_err = 1;
+ int core_err = 0;
+
+ if (!is_update || need_period_modifier(iforce, effect)) {
+ param1_err = make_period_modifier(iforce, mod1_chunk,
+ is_update,
+ effect->u.periodic.magnitude, effect->u.periodic.offset,
+ effect->u.periodic.period, effect->u.periodic.phase);
+ if (param1_err) return param1_err;
+ set_bit(FF_MOD1_IS_USED, core_effect->flags);
+ }
+
+ if (!is_update || need_envelope_modifier(iforce, effect)) {
+ param2_err = make_envelope_modifier(iforce, mod2_chunk,
+ is_update,
+ effect->u.periodic.envelope.attack_length,
+ effect->u.periodic.envelope.attack_level,
+ effect->u.periodic.envelope.fade_length,
+ effect->u.periodic.envelope.fade_level);
+ if (param2_err) return param2_err;
+ set_bit(FF_MOD2_IS_USED, core_effect->flags);
+ }
+
+ switch (effect->u.periodic.waveform) {
+ case FF_SQUARE: wave_code = 0x20; break;
+ case FF_TRIANGLE: wave_code = 0x21; break;
+ case FF_SINE: wave_code = 0x22; break;
+ case FF_SAW_UP: wave_code = 0x23; break;
+ case FF_SAW_DOWN: wave_code = 0x24; break;
+ default: wave_code = 0x20; break;
+ }
+
+ if (!is_update || need_core(iforce, effect)) {
+ core_err = make_core(iforce, effect->id,
+ mod1_chunk->start,
+ mod2_chunk->start,
+ wave_code,
+ 0x20,
+ effect->replay.length,
+ effect->replay.delay,
+ effect->trigger.button,
+ effect->trigger.interval,
+ effect->direction);
+ }
+
+ /* If one of the parameter creation failed, we already returned an
+ * error code.
+ * If the core creation failed, we return its error code.
+ * Else: if one parameter at least was created, we return 0
+ * else we return 1;
+ */
+ return core_err < 0 ? core_err : (param1_err && param2_err);
+}
+
+/*
+ * Upload a constant force effect
+ * Return value:
+ * <0 Error code
+ * 0 Ok, effect created or updated
+ * 1 effect did not change since last upload, and no packet was therefore sent
+ */
+int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+ int core_id = effect->id;
+ struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+ struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+ struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+ int param1_err = 1;
+ int param2_err = 1;
+ int core_err = 0;
+
+ if (!is_update || need_magnitude_modifier(iforce, effect)) {
+ param1_err = make_magnitude_modifier(iforce, mod1_chunk,
+ is_update,
+ effect->u.constant.level);
+ if (param1_err) return param1_err;
+ set_bit(FF_MOD1_IS_USED, core_effect->flags);
+ }
+
+ if (!is_update || need_envelope_modifier(iforce, effect)) {
+ param2_err = make_envelope_modifier(iforce, mod2_chunk,
+ is_update,
+ effect->u.constant.envelope.attack_length,
+ effect->u.constant.envelope.attack_level,
+ effect->u.constant.envelope.fade_length,
+ effect->u.constant.envelope.fade_level);
+ if (param2_err) return param2_err;
+ set_bit(FF_MOD2_IS_USED, core_effect->flags);
+ }
+
+ if (!is_update || need_core(iforce, effect)) {
+ core_err = make_core(iforce, effect->id,
+ mod1_chunk->start,
+ mod2_chunk->start,
+ 0x00,
+ 0x20,
+ effect->replay.length,
+ effect->replay.delay,
+ effect->trigger.button,
+ effect->trigger.interval,
+ effect->direction);
+ }
+
+ /* If one of the parameter creation failed, we already returned an
+ * error code.
+ * If the core creation failed, we return its error code.
+ * Else: if one parameter at least was created, we return 0
+ * else we return 1;
+ */
+ return core_err < 0 ? core_err : (param1_err && param2_err);
+}
+
+/*
+ * Upload an condition effect. Those are for example friction, inertia, springs...
+ */
+int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+ int core_id = effect->id;
+ struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+ struct resource* mod1_chunk = &(core_effect->mod1_chunk);
+ struct resource* mod2_chunk = &(core_effect->mod2_chunk);
+ u8 type;
+ int param_err = 1;
+ int core_err = 0;
+
+ switch (effect->type) {
+ case FF_SPRING: type = 0x40; break;
+ case FF_DAMPER: type = 0x41; break;
+ default: return -1;
+ }
+
+ if (!is_update || need_condition_modifier(iforce, effect)) {
+ param_err = make_condition_modifier(iforce, mod1_chunk,
+ is_update,
+ effect->u.condition[0].right_saturation,
+ effect->u.condition[0].left_saturation,
+ effect->u.condition[0].right_coeff,
+ effect->u.condition[0].left_coeff,
+ effect->u.condition[0].deadband,
+ effect->u.condition[0].center);
+ if (param_err) return param_err;
+ set_bit(FF_MOD1_IS_USED, core_effect->flags);
+
+ param_err = make_condition_modifier(iforce, mod2_chunk,
+ is_update,
+ effect->u.condition[1].right_saturation,
+ effect->u.condition[1].left_saturation,
+ effect->u.condition[1].right_coeff,
+ effect->u.condition[1].left_coeff,
+ effect->u.condition[1].deadband,
+ effect->u.condition[1].center);
+ if (param_err) return param_err;
+ set_bit(FF_MOD2_IS_USED, core_effect->flags);
+
+ }
+
+ if (!is_update || need_core(iforce, effect)) {
+ core_err = make_core(iforce, effect->id,
+ mod1_chunk->start, mod2_chunk->start,
+ type, 0xc0,
+ effect->replay.length, effect->replay.delay,
+ effect->trigger.button, effect->trigger.interval,
+ effect->direction);
+ }
+
+ /* If the parameter creation failed, we already returned an
+ * error code.
+ * If the core creation failed, we return its error code.
+ * Else: if a parameter was created, we return 0
+ * else we return 1;
+ */
+ return core_err < 0 ? core_err : param_err;
+}
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
new file mode 100644
index 000000000000..45b7c001e066
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -0,0 +1,543 @@
+/*
+ * $Id: iforce-main.c,v 1.18 2002/06/09 11:03:03 jdeneux Exp $
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ * USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <deneux@ifrance.com>");
+MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
+MODULE_LICENSE("GPL");
+
+static signed short btn_joystick[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+ BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+
+static signed short btn_avb_pegasus[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+ BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
+
+static signed short btn_wheel[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+ BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+
+static signed short btn_avb_tw[] =
+{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE,
+ BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
+
+static signed short btn_avb_wheel[] =
+{ BTN_GEAR_DOWN, BTN_GEAR_UP, BTN_BASE, BTN_BASE2, BTN_BASE3,
+ BTN_BASE4, BTN_BASE5, BTN_BASE6, -1 };
+
+static signed short abs_joystick[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short abs_avb_pegasus[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y,
+ ABS_HAT1X, ABS_HAT1Y, -1 };
+
+static signed short abs_wheel[] =
+{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short ff_iforce[] =
+{ FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_DAMPER,
+ FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN,
+ FF_AUTOCENTER, -1 };
+
+static struct iforce_device iforce_device[] = {
+ { 0x044f, 0xa01c, "Thrustmaster Motor Sport GT", btn_wheel, abs_wheel, ff_iforce },
+ { 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce },
+ { 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce },
+ { 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_avb_pegasus, abs_avb_pegasus, ff_iforce },
+ { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_avb_wheel, abs_wheel, ff_iforce },
+ { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_avb_tw, abs_wheel, ff_iforce }, //?
+ { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //?
+ { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //?
+ { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
+ { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce }
+};
+
+
+
+static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct iforce* iforce = (struct iforce*)(dev->private);
+ unsigned char data[3];
+
+ if (type != EV_FF)
+ return -1;
+
+ switch (code) {
+
+ case FF_GAIN:
+
+ data[0] = value >> 9;
+ iforce_send_packet(iforce, FF_CMD_GAIN, data);
+
+ return 0;
+
+ case FF_AUTOCENTER:
+
+ data[0] = 0x03;
+ data[1] = value >> 9;
+ iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+ data[0] = 0x04;
+ data[1] = 0x01;
+ iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+ return 0;
+
+ default: /* Play or stop an effect */
+
+ if (!CHECK_OWNERSHIP(code, iforce)) {
+ return -1;
+ }
+ if (value > 0) {
+ set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
+ }
+ else {
+ clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
+ }
+
+ iforce_control_playback(iforce, code, value);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Function called when an ioctl is performed on the event dev entry.
+ * It uploads an effect to the device
+ */
+static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
+{
+ struct iforce* iforce = (struct iforce*)(dev->private);
+ int id;
+ int ret;
+ int is_update;
+
+/* Check this effect type is supported by this device */
+ if (!test_bit(effect->type, iforce->dev.ffbit))
+ return -EINVAL;
+
+/*
+ * If we want to create a new effect, get a free id
+ */
+ if (effect->id == -1) {
+
+ for (id=0; id < FF_EFFECTS_MAX; ++id)
+ if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break;
+
+ if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max)
+ return -ENOMEM;
+
+ effect->id = id;
+ iforce->core_effects[id].owner = current->pid;
+ iforce->core_effects[id].flags[0] = (1<<FF_CORE_IS_USED); /* Only IS_USED bit must be set */
+
+ is_update = FALSE;
+ }
+ else {
+ /* We want to update an effect */
+ if (!CHECK_OWNERSHIP(effect->id, iforce)) return -EACCES;
+
+ /* Parameter type cannot be updated */
+ if (effect->type != iforce->core_effects[effect->id].effect.type)
+ return -EINVAL;
+
+ /* Check the effect is not already being updated */
+ if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) {
+ return -EAGAIN;
+ }
+
+ is_update = TRUE;
+ }
+
+/*
+ * Upload the effect
+ */
+ switch (effect->type) {
+
+ case FF_PERIODIC:
+ ret = iforce_upload_periodic(iforce, effect, is_update);
+ break;
+
+ case FF_CONSTANT:
+ ret = iforce_upload_constant(iforce, effect, is_update);
+ break;
+
+ case FF_SPRING:
+ case FF_DAMPER:
+ ret = iforce_upload_condition(iforce, effect, is_update);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ if (ret == 0) {
+ /* A packet was sent, forbid new updates until we are notified
+ * that the packet was updated
+ */
+ set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags);
+ }
+ iforce->core_effects[effect->id].effect = *effect;
+ return ret;
+}
+
+/*
+ * Erases an effect: it frees the effect id and mark as unused the memory
+ * allocated for the parameters
+ */
+static int iforce_erase_effect(struct input_dev *dev, int effect_id)
+{
+ struct iforce* iforce = (struct iforce*)(dev->private);
+ int err = 0;
+ struct iforce_core_effect* core_effect;
+
+ /* Check who is trying to erase this effect */
+ if (iforce->core_effects[effect_id].owner != current->pid) {
+ printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner);
+ return -EACCES;
+ }
+
+ if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
+ return -EINVAL;
+
+ core_effect = iforce->core_effects + effect_id;
+
+ if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
+ err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk));
+
+ if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
+ err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk));
+
+ /*TODO: remember to change that if more FF_MOD* bits are added */
+ core_effect->flags[0] = 0;
+
+ return err;
+}
+
+static int iforce_open(struct input_dev *dev)
+{
+ struct iforce *iforce = dev->private;
+
+ switch (iforce->bus) {
+#ifdef IFORCE_USB
+ case IFORCE_USB:
+ iforce->irq->dev = iforce->usbdev;
+ if (usb_submit_urb(iforce->irq, GFP_KERNEL))
+ return -EIO;
+ break;
+#endif
+ }
+
+ /* Enable force feedback */
+ iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+
+ return 0;
+}
+
+static int iforce_flush(struct input_dev *dev, struct file *file)
+{
+ struct iforce *iforce = dev->private;
+ int i;
+
+ /* Erase all effects this process owns */
+ for (i=0; i<dev->ff_effects_max; ++i) {
+
+ if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
+ current->pid == iforce->core_effects[i].owner) {
+
+ /* Stop effect */
+ input_report_ff(dev, i, 0);
+
+ /* Free ressources assigned to effect */
+ if (iforce_erase_effect(dev, i)) {
+ printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i);
+ }
+ }
+
+ }
+ return 0;
+}
+
+static void iforce_release(struct input_dev *dev)
+{
+ struct iforce *iforce = dev->private;
+ int i;
+
+ /* Check: no effect should be present in memory */
+ for (i=0; i<dev->ff_effects_max; ++i) {
+ if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags))
+ break;
+ }
+ if (i<dev->ff_effects_max) {
+ printk(KERN_WARNING "iforce_release: Device still owns effects\n");
+ }
+
+ /* Disable force feedback playback */
+ iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
+
+ switch (iforce->bus) {
+#ifdef IFORCE_USB
+ case IFORCE_USB:
+ usb_unlink_urb(iforce->irq);
+
+ /* The device was unplugged before the file
+ * was released */
+ if (iforce->usbdev == NULL) {
+ iforce_delete_device(iforce);
+ kfree(iforce);
+ }
+ break;
+#endif
+ }
+}
+
+void iforce_delete_device(struct iforce *iforce)
+{
+ switch (iforce->bus) {
+#ifdef IFORCE_USB
+ case IFORCE_USB:
+ iforce_usb_delete(iforce);
+ break;
+#endif
+#ifdef IFORCE_232
+ case IFORCE_232:
+ //TODO: Wait for the last packets to be sent
+ break;
+#endif
+ }
+}
+
+int iforce_init_device(struct iforce *iforce)
+{
+ unsigned char c[] = "CEOV";
+ int i;
+
+ init_waitqueue_head(&iforce->wait);
+ spin_lock_init(&iforce->xmit_lock);
+ init_MUTEX(&iforce->mem_mutex);
+ iforce->xmit.buf = iforce->xmit_data;
+
+ iforce->dev.ff_effects_max = 10;
+
+/*
+ * Input device fields.
+ */
+
+ iforce->dev.idbus = BUS_USB;
+ iforce->dev.private = iforce;
+ iforce->dev.name = "Unknown I-Force device";
+ iforce->dev.open = iforce_open;
+ iforce->dev.close = iforce_release;
+ iforce->dev.flush = iforce_flush;
+ iforce->dev.event = iforce_input_event;
+ iforce->dev.upload_effect = iforce_upload_effect;
+ iforce->dev.erase_effect = iforce_erase_effect;
+
+/*
+ * On-device memory allocation.
+ */
+
+ iforce->device_memory.name = "I-Force device effect memory";
+ iforce->device_memory.start = 0;
+ iforce->device_memory.end = 200;
+ iforce->device_memory.flags = IORESOURCE_MEM;
+ iforce->device_memory.parent = NULL;
+ iforce->device_memory.child = NULL;
+ iforce->device_memory.sibling = NULL;
+
+/*
+ * Wait until device ready - until it sends its first response.
+ */
+
+ for (i = 0; i < 20; i++)
+ if (!iforce_get_id_packet(iforce, "O"))
+ break;
+
+ if (i == 20) { /* 5 seconds */
+ printk(KERN_ERR "iforce-main.c: Timeout waiting for response from device.\n");
+ return -1;
+ }
+
+/*
+ * Get device info.
+ */
+
+ if (!iforce_get_id_packet(iforce, "M"))
+ iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1];
+ else
+ printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet M\n");
+
+ if (!iforce_get_id_packet(iforce, "P"))
+ iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1];
+ else
+ printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet P\n");
+
+ if (!iforce_get_id_packet(iforce, "B"))
+ iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
+ else
+ printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n");
+
+ if (!iforce_get_id_packet(iforce, "N"))
+ iforce->dev.ff_effects_max = iforce->edata[1];
+ else
+ printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n");
+
+ /* Check if the device can store more effects than the driver can really handle */
+ if (iforce->dev.ff_effects_max > FF_EFFECTS_MAX) {
+ printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n",
+ iforce->dev.ff_effects_max, FF_EFFECTS_MAX);
+ iforce->dev.ff_effects_max = FF_EFFECTS_MAX;
+ }
+
+/*
+ * Display additional info.
+ */
+
+ for (i = 0; c[i]; i++)
+ if (!iforce_get_id_packet(iforce, c + i))
+ iforce_dump_packet("info", iforce->ecmd, iforce->edata);
+
+/*
+ * Disable spring, enable force feedback.
+ * FIXME: We should use iforce_set_autocenter() et al here.
+ */
+
+ iforce_send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000");
+
+/*
+ * Find appropriate device entry
+ */
+
+ for (i = 0; iforce_device[i].idvendor; i++)
+ if (iforce_device[i].idvendor == iforce->dev.idvendor &&
+ iforce_device[i].idproduct == iforce->dev.idproduct)
+ break;
+
+ iforce->type = iforce_device + i;
+ iforce->dev.name = iforce->type->name;
+
+/*
+ * Set input device bitfields and ranges.
+ */
+
+ iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS);
+
+ for (i = 0; iforce->type->btn[i] >= 0; i++) {
+ signed short t = iforce->type->btn[i];
+ set_bit(t, iforce->dev.keybit);
+ }
+ set_bit(BTN_DEAD, iforce->dev.keybit);
+
+ for (i = 0; iforce->type->abs[i] >= 0; i++) {
+
+ signed short t = iforce->type->abs[i];
+ set_bit(t, iforce->dev.absbit);
+
+ switch (t) {
+
+ case ABS_X:
+ case ABS_Y:
+ case ABS_WHEEL:
+
+ iforce->dev.absmax[t] = 1920;
+ iforce->dev.absmin[t] = -1920;
+ iforce->dev.absflat[t] = 128;
+ iforce->dev.absfuzz[t] = 16;
+
+ set_bit(t, iforce->dev.ffbit);
+ break;
+
+ case ABS_THROTTLE:
+ case ABS_GAS:
+ case ABS_BRAKE:
+
+ iforce->dev.absmax[t] = 255;
+ iforce->dev.absmin[t] = 0;
+ break;
+
+ case ABS_RUDDER:
+
+ iforce->dev.absmax[t] = 127;
+ iforce->dev.absmin[t] = -128;
+ break;
+
+ case ABS_HAT0X:
+ case ABS_HAT0Y:
+ case ABS_HAT1X:
+ case ABS_HAT1Y:
+ iforce->dev.absmax[t] = 1;
+ iforce->dev.absmin[t] = -1;
+ break;
+ }
+ }
+
+ for (i = 0; iforce->type->ff[i] >= 0; i++)
+ set_bit(iforce->type->ff[i], iforce->dev.ffbit);
+
+/*
+ * Register input device.
+ */
+
+ input_register_device(&iforce->dev);
+
+ printk(KERN_DEBUG "iforce->dev.open = %p\n", iforce->dev.open);
+
+ printk(KERN_INFO "input: %s [%d effects, %ld bytes memory]\n",
+ iforce->dev.name, iforce->dev.ff_effects_max,
+ iforce->device_memory.end);
+
+ return 0;
+}
+
+static int __init iforce_init(void)
+{
+#ifdef IFORCE_USB
+ usb_register(&iforce_usb_driver);
+#endif
+#ifdef IFORCE_232
+ serio_register_device(&iforce_serio_dev);
+#endif
+ return 0;
+}
+
+static void __exit iforce_exit(void)
+{
+#ifdef IFORCE_USB
+ usb_deregister(&iforce_usb_driver);
+#endif
+#ifdef IFORCE_232
+ serio_unregister_device(&iforce_serio_dev);
+#endif
+}
+
+module_init(iforce_init);
+module_exit(iforce_exit);
diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c
new file mode 100644
index 000000000000..922894bb13ea
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-packets.c
@@ -0,0 +1,314 @@
+/*
+ * $Id: iforce-packets.c,v 1.15 2002/06/09 11:08:04 jdeneux Exp $
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ * USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+static struct {
+ __s32 x;
+ __s32 y;
+} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+
+void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data)
+{
+ int i;
+
+ printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
+ for (i = 0; i < LO(cmd); i++)
+ printk("%02x ", data[i]);
+ printk(")\n");
+}
+
+/*
+ * Send a packet of bytes to the device
+ */
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
+{
+ /* Copy data to buffer */
+ int n = LO(cmd);
+ int c;
+ int empty;
+ int head, tail;
+ unsigned long flags;
+
+/*
+ * Update head and tail of xmit buffer
+ */
+ spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+ head = iforce->xmit.head;
+ tail = iforce->xmit.tail;
+
+ if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) {
+ printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n");
+ spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+ return -1;
+ }
+
+ empty = head == tail;
+ XMIT_INC(iforce->xmit.head, n+2);
+
+/*
+ * Store packet in xmit buffer
+ */
+ iforce->xmit.buf[head] = HI(cmd);
+ XMIT_INC(head, 1);
+ iforce->xmit.buf[head] = LO(cmd);
+ XMIT_INC(head, 1);
+
+ c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE);
+ if (n < c) c=n;
+
+ memcpy(&iforce->xmit.buf[head],
+ data,
+ c);
+ if (n != c) {
+ memcpy(&iforce->xmit.buf[0],
+ data + c,
+ n - c);
+ }
+ XMIT_INC(head, n);
+
+ spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+/*
+ * If necessary, start the transmission
+ */
+ switch (iforce->bus) {
+
+#ifdef IFORCE_232
+ case IFORCE_232:
+ if (empty)
+ iforce_serial_xmit(iforce);
+ break;
+#endif
+#ifdef IFORCE_USB
+ case IFORCE_USB:
+
+ if (iforce->usbdev && empty &&
+ !test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
+
+ iforce_usb_xmit(iforce);
+ }
+ break;
+#endif
+ }
+ return 0;
+}
+
+/* Start or stop an effect */
+int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
+{
+ unsigned char data[3];
+
+printk(KERN_DEBUG "iforce-packets.c: control_playback %d %d\n", id, value);
+
+ data[0] = LO(id);
+ data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
+ data[2] = LO(value);
+ return iforce_send_packet(iforce, FF_CMD_PLAY, data);
+}
+
+/* Mark an effect that was being updated as ready. That means it can be updated
+ * again */
+static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
+{
+ int i;
+ for (i=0; i<iforce->dev.ff_effects_max; ++i) {
+ if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
+ (iforce->core_effects[i].mod1_chunk.start == addr ||
+ iforce->core_effects[i].mod2_chunk.start == addr)) {
+ clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags);
+ return 0;
+ }
+ }
+ printk(KERN_WARNING "iforce-packets.c: unused effect %04x updated !!!\n", addr);
+ return -1;
+}
+
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
+{
+ struct input_dev *dev = &iforce->dev;
+ int i;
+ static int being_used = 0;
+
+ if (being_used)
+ printk(KERN_WARNING "iforce-packets.c: re-entrant call to iforce_process %d\n", being_used);
+ being_used++;
+
+#ifdef IFORCE_232
+ if (HI(iforce->expect_packet) == HI(cmd)) {
+ iforce->expect_packet = 0;
+ iforce->ecmd = cmd;
+ memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
+ if (waitqueue_active(&iforce->wait))
+ wake_up(&iforce->wait);
+ }
+#endif
+
+ if (!iforce->type) {
+ being_used--;
+ return;
+ }
+
+ switch (HI(cmd)) {
+
+ case 0x01: /* joystick position data */
+ case 0x03: /* wheel position data */
+
+ if (HI(cmd) == 1) {
+ input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
+ input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
+ input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
+ if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
+ input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
+ } else {
+ input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
+ input_report_abs(dev, ABS_GAS, 255 - data[2]);
+ input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
+ }
+
+ input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
+ input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
+
+ for (i = 0; iforce->type->btn[i] >= 0; i++)
+ input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
+
+ /* If there are untouched bits left, interpret them as the second hat */
+ if (i <= 8) {
+ int btns = data[6];
+ if (test_bit(ABS_HAT1X, dev->absbit)) {
+ if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1);
+ else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1);
+ else input_report_abs(dev, ABS_HAT1X, 0);
+ }
+ if (test_bit(ABS_HAT1Y, dev->absbit)) {
+ if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1);
+ else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1);
+ else input_report_abs(dev, ABS_HAT1Y, 0);
+ }
+ }
+
+ break;
+
+ case 0x02: /* status report */
+ input_report_key(dev, BTN_DEAD, data[0] & 0x02);
+
+ /* Check if an effect was just started or stopped */
+ i = data[1] & 0x7f;
+ if (data[1] & 0x80) {
+ if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+ /* Report play event */
+ input_report_ff_status(dev, i, FF_STATUS_PLAYING);
+ }
+ }
+ else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+ /* Report stop event */
+ input_report_ff_status(dev, i, FF_STATUS_STOPPED);
+ }
+ if (LO(cmd) > 3) {
+ int j;
+ for (j=3; j<LO(cmd); j+=2) {
+ mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
+ }
+ }
+ break;
+ }
+ being_used--;
+}
+
+int iforce_get_id_packet(struct iforce *iforce, char *packet)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int timeout = HZ; /* 1 second */
+
+ switch (iforce->bus) {
+
+ case IFORCE_USB:
+
+#ifdef IFORCE_USB
+ iforce->cr.bRequest = packet[0];
+ iforce->ctrl->dev = iforce->usbdev;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&iforce->wait, &wait);
+
+ if (usb_submit_urb(iforce->ctrl, GFP_KERNEL)) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&iforce->wait, &wait);
+ return -1;
+ }
+
+ while (timeout && iforce->ctrl->status == -EINPROGRESS)
+ timeout = schedule_timeout(timeout);
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&iforce->wait, &wait);
+
+ if (!timeout) {
+ usb_unlink_urb(iforce->ctrl);
+ return -1;
+ }
+#else
+ printk(KERN_ERR "iforce_get_id_packet: iforce->bus = USB!\n");
+#endif
+ break;
+
+ case IFORCE_232:
+
+#ifdef IFORCE_232
+ iforce->expect_packet = FF_CMD_QUERY;
+ iforce_send_packet(iforce, FF_CMD_QUERY, packet);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&iforce->wait, &wait);
+
+ while (timeout && iforce->expect_packet)
+ timeout = schedule_timeout(timeout);
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&iforce->wait, &wait);
+
+ if (!timeout) {
+ iforce->expect_packet = 0;
+ return -1;
+ }
+#else
+ printk(KERN_ERR "iforce_get_id_packet: iforce->bus = SERIO!\n");
+#endif
+ break;
+
+ default:
+ printk(KERN_ERR "iforce_get_id_packet: iforce->bus = %d\n",
+ iforce->bus);
+ break;
+ }
+
+ return -(iforce->edata[0] != packet[0]);
+}
+
diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c
new file mode 100644
index 000000000000..31e21fb431f6
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-serio.c
@@ -0,0 +1,166 @@
+/*
+ * $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
+ *
+ * USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+void iforce_serial_xmit(struct iforce *iforce)
+{
+ unsigned char cs;
+ int i;
+ unsigned long flags;
+
+ if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
+ set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags);
+ return;
+ }
+
+ spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+again:
+ if (iforce->xmit.head == iforce->xmit.tail) {
+ clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+ spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+ return;
+ }
+
+ cs = 0x2b;
+
+ serio_write(iforce->serio, 0x2b);
+
+ serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+ cs ^= iforce->xmit.buf[iforce->xmit.tail];
+ XMIT_INC(iforce->xmit.tail, 1);
+
+ for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) {
+ serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+ cs ^= iforce->xmit.buf[iforce->xmit.tail];
+ XMIT_INC(iforce->xmit.tail, 1);
+ }
+
+ serio_write(iforce->serio, cs);
+
+ if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
+ goto again;
+
+ clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+
+ spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+}
+
+static void iforce_serio_write_wakeup(struct serio *serio)
+{
+ iforce_serial_xmit((struct iforce *)serio->private);
+}
+
+static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct iforce* iforce = serio->private;
+
+ if (!iforce->pkt) {
+ if (data != 0x2b) {
+ return;
+ }
+ iforce->pkt = 1;
+ return;
+ }
+
+ if (!iforce->id) {
+ if (data > 3 && data != 0xff) {
+ iforce->pkt = 0;
+ return;
+ }
+ iforce->id = data;
+ return;
+ }
+
+ if (!iforce->len) {
+ if (data > IFORCE_MAX_LENGTH) {
+ iforce->pkt = 0;
+ iforce->id = 0;
+ return;
+ }
+ iforce->len = data;
+ return;
+ }
+
+ if (iforce->idx < iforce->len) {
+ iforce->csum += iforce->data[iforce->idx++] = data;
+ return;
+ }
+
+ if (iforce->idx == iforce->len) {
+ iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data);
+ iforce->pkt = 0;
+ iforce->id = 0;
+ iforce->len = 0;
+ iforce->idx = 0;
+ iforce->csum = 0;
+ }
+}
+
+static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct iforce *iforce;
+ if (serio->type != (SERIO_RS232 | SERIO_IFORCE))
+ return;
+
+ if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return;
+ memset(iforce, 0, sizeof(struct iforce));
+
+ iforce->bus = IFORCE_232;
+ iforce->serio = serio;
+ serio->private = iforce;
+
+ if (serio_open(serio, dev)) {
+ kfree(iforce);
+ return;
+ }
+
+ if (iforce_init_device(iforce)) {
+ serio_close(serio);
+ kfree(iforce);
+ return;
+ }
+}
+
+static void iforce_serio_disconnect(struct serio *serio)
+{
+ struct iforce* iforce = serio->private;
+
+ input_unregister_device(&iforce->dev);
+ serio_close(serio);
+ kfree(iforce);
+}
+
+struct serio_dev iforce_serio_dev = {
+ write_wakeup: iforce_serio_write_wakeup,
+ interrupt: iforce_serio_irq,
+ connect: iforce_serio_connect,
+ disconnect: iforce_serio_disconnect,
+};
diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c
new file mode 100644
index 000000000000..760534a89f72
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -0,0 +1,214 @@
+ /*
+ * $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ * USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+void iforce_usb_xmit(struct iforce *iforce)
+{
+ int n, c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+ if (iforce->xmit.head == iforce->xmit.tail) {
+ clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+ spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+ return;
+ }
+
+ ((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
+ XMIT_INC(iforce->xmit.tail, 1);
+ n = iforce->xmit.buf[iforce->xmit.tail];
+ XMIT_INC(iforce->xmit.tail, 1);
+
+ iforce->out->transfer_buffer_length = n + 1;
+ iforce->out->dev = iforce->usbdev;
+
+ /* Copy rest of data then */
+ c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
+ if (n < c) c=n;
+
+ memcpy(iforce->out->transfer_buffer + 1,
+ &iforce->xmit.buf[iforce->xmit.tail],
+ c);
+ if (n != c) {
+ memcpy(iforce->out->transfer_buffer + 1 + c,
+ &iforce->xmit.buf[0],
+ n-c);
+ }
+ XMIT_INC(iforce->xmit.tail, n);
+
+ if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
+ printk(KERN_WARNING "iforce-usb.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n);
+ }
+
+ /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
+ * As long as the urb completion handler is not called, the transmiting
+ * is considered to be running */
+ spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+}
+
+static void iforce_usb_irq(struct urb *urb)
+{
+ struct iforce *iforce = urb->context;
+ if (urb->status) return;
+ iforce_process_packet(iforce,
+ (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
+}
+
+static void iforce_usb_out(struct urb *urb)
+{
+ struct iforce *iforce = urb->context;
+
+ if (urb->status) {
+ printk(KERN_DEBUG "iforce_usb_out: urb->status %d, exiting", urb->status);
+ return;
+ }
+
+ iforce_usb_xmit(iforce);
+
+ if (waitqueue_active(&iforce->wait))
+ wake_up(&iforce->wait);
+}
+
+static void iforce_usb_ctrl(struct urb *urb)
+{
+ struct iforce *iforce = urb->context;
+ if (urb->status) return;
+ iforce->ecmd = 0xff00 | urb->actual_length;
+ if (waitqueue_active(&iforce->wait))
+ wake_up(&iforce->wait);
+}
+
+static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *epirq, *epout;
+ struct iforce *iforce;
+
+ epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+ epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1;
+
+ if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
+ goto fail;
+
+ memset(iforce, 0, sizeof(struct iforce));
+
+ if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL))) {
+ goto fail;
+ }
+
+ if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL))) {
+ goto fail;
+ }
+
+ if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL))) {
+ goto fail;
+ }
+
+ iforce->bus = IFORCE_USB;
+ iforce->usbdev = dev;
+
+ iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
+ iforce->cr.wIndex = 0;
+ iforce->cr.wLength = 16;
+
+ usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
+ iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
+
+ usb_fill_bulk_urb(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress),
+ iforce + 1, 32, iforce_usb_out, iforce);
+
+ usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
+ (void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
+
+ if (iforce_init_device(iforce)) goto fail;
+
+ return iforce;
+
+fail:
+ if (iforce) {
+ if (iforce->irq) usb_free_urb(iforce->irq);
+ if (iforce->out) usb_free_urb(iforce->out);
+ if (iforce->ctrl) usb_free_urb(iforce->ctrl);
+ kfree(iforce);
+ }
+
+ return NULL;
+}
+
+/* Called by iforce_delete() */
+void iforce_usb_delete(struct iforce* iforce)
+{
+ usb_unlink_urb(iforce->irq);
+/* Is it ok to unlink those ? */
+ usb_unlink_urb(iforce->out);
+ usb_unlink_urb(iforce->ctrl);
+
+ usb_free_urb(iforce->irq);
+ usb_free_urb(iforce->out);
+ usb_free_urb(iforce->ctrl);
+}
+
+static void iforce_usb_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct iforce *iforce = ptr;
+ int open = iforce->dev.handle->open;
+
+ iforce->usbdev = NULL;
+ input_unregister_device(&iforce->dev);
+
+ if (!open) {
+ iforce_delete_device(iforce);
+ kfree(iforce);
+ }
+}
+
+static struct usb_device_id iforce_usb_ids [] = {
+ { USB_DEVICE(0x044f, 0xa01c) }, /* Thrustmaster Motor Sport GT */
+ { USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */
+ { USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */
+ { USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */
+ { USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */
+ { USB_DEVICE(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */
+ { USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */
+ { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */
+ { USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
+
+struct usb_driver iforce_usb_driver = {
+ owner: THIS_MODULE,
+ name: "iforce",
+ probe: iforce_usb_probe,
+ disconnect: iforce_usb_disconnect,
+ id_table: iforce_usb_ids,
+};
diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h
new file mode 100644
index 000000000000..fec1b258225c
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce.h
@@ -0,0 +1,194 @@
+/*
+ * $Id: iforce.h,v 1.12 2002/06/09 11:08:04 jdeneux Exp $
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ * USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/serio.h>
+#include <linux/config.h>
+#include <linux/circ_buf.h>
+#include <asm/semaphore.h>
+
+/* FF: This module provides arbitrary resource management routines.
+ * I use it to manage the device's memory.
+ * Despite the name of this module, I am *not* going to access the ioports.
+ */
+#include <linux/ioport.h>
+
+#define IFORCE_MAX_LENGTH 16
+
+#if defined(CONFIG_JOYSTICK_IFORCE_232)
+#define IFORCE_232 1
+#endif
+#if defined(CONFIG_JOYSTICK_IFORCE_USB)
+#define IFORCE_USB 2
+#endif
+
+#define FALSE 0
+#define TRUE 1
+
+#define FF_EFFECTS_MAX 32
+
+/* Each force feedback effect is made of one core effect, which can be
+ * associated to at most to effect modifiers
+ */
+#define FF_MOD1_IS_USED 0
+#define FF_MOD2_IS_USED 1
+#define FF_CORE_IS_USED 2
+#define FF_CORE_IS_PLAYED 3 /* Effect is currently being played */
+#define FF_CORE_SHOULD_PLAY 4 /* User wants the effect to be played */
+#define FF_CORE_UPDATE 5 /* Effect is being updated */
+#define FF_MODCORE_MAX 5
+
+#define CHECK_OWNERSHIP(i, iforce) \
+ ((i) < FF_EFFECTS_MAX && i >= 0 && \
+ test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \
+ (current->pid == 0 || \
+ (iforce)->core_effects[(i)].owner == current->pid))
+
+struct iforce_core_effect {
+ /* Information about where modifiers are stored in the device's memory */
+ struct resource mod1_chunk;
+ struct resource mod2_chunk;
+ unsigned long flags[NBITS(FF_MODCORE_MAX)];
+ pid_t owner;
+ /* Used to keep track of parameters of an effect. They are needed
+ * to know what parts of an effect changed in an update operation.
+ * We try to send only parameter packets if possible, as sending
+ * effect parameter requires the effect to be stoped and restarted
+ */
+ struct ff_effect effect;
+};
+
+#define FF_CMD_EFFECT 0x010e
+#define FF_CMD_ENVELOPE 0x0208
+#define FF_CMD_MAGNITUDE 0x0303
+#define FF_CMD_PERIOD 0x0407
+#define FF_CMD_CONDITION 0x050a
+
+#define FF_CMD_AUTOCENTER 0x4002
+#define FF_CMD_PLAY 0x4103
+#define FF_CMD_ENABLE 0x4201
+#define FF_CMD_GAIN 0x4301
+
+#define FF_CMD_QUERY 0xff01
+
+/* Buffer for async write */
+#define XMIT_SIZE 256
+#define XMIT_INC(var, n) (var)+=n; (var)&= XMIT_SIZE -1
+/* iforce::xmit_flags */
+#define IFORCE_XMIT_RUNNING 0
+#define IFORCE_XMIT_AGAIN 1
+
+struct iforce_device {
+ u16 idvendor;
+ u16 idproduct;
+ char *name;
+ signed short *btn;
+ signed short *abs;
+ signed short *ff;
+};
+
+struct iforce {
+ struct input_dev dev; /* Input device interface */
+ struct iforce_device *type;
+ int bus;
+
+ unsigned char data[IFORCE_MAX_LENGTH];
+ unsigned char edata[IFORCE_MAX_LENGTH];
+ u16 ecmd;
+ u16 expect_packet;
+
+#ifdef IFORCE_232
+ struct serio *serio; /* RS232 transfer */
+ int idx, pkt, len, id;
+ unsigned char csum;
+#endif
+#ifdef IFORCE_USB
+ struct usb_device *usbdev; /* USB transfer */
+ struct urb *irq, *out, *ctrl;
+ struct usb_ctrlrequest cr;
+#endif
+ spinlock_t xmit_lock;
+ /* Buffer used for asynchronous sending of bytes to the device */
+ struct circ_buf xmit;
+ unsigned char xmit_data[XMIT_SIZE];
+ long xmit_flags[1];
+
+ /* Force Feedback */
+ wait_queue_head_t wait;
+ struct resource device_memory;
+ struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
+ struct semaphore mem_mutex;
+};
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a) ((unsigned char)((a) >> 8))
+#define LO(a) ((unsigned char)((a) & 0xff))
+
+/* For many parameters, it seems that 0x80 is a special value that should
+ * be avoided. Instead, we replace this value by 0x7f
+ */
+#define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8))
+
+/* Encode a time value */
+#define TIME_SCALE(a) (a)
+
+
+/* Public functions */
+/* iforce-serio.c */
+void iforce_serial_xmit(struct iforce *iforce);
+
+/* iforce-usb.c */
+void iforce_usb_xmit(struct iforce *iforce);
+void iforce_usb_delete(struct iforce *iforce);
+
+/* iforce-main.c */
+int iforce_init_device(struct iforce *iforce);
+void iforce_delete_device(struct iforce *iforce);
+
+/* iforce-packets.c */
+int iforce_control_playback(struct iforce*, u16 id, unsigned int);
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data);
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
+void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ;
+int iforce_get_id_packet(struct iforce *iforce, char *packet);
+
+/* iforce-ff.c */
+int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update);
+int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update);
+int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update);
+
+/* Public variables */
+extern struct serio_dev iforce_serio_dev;
+extern struct usb_driver iforce_usb_driver;
diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c
new file mode 100644
index 000000000000..d64493c0cc04
--- /dev/null
+++ b/drivers/input/joystick/joydump.c
@@ -0,0 +1,152 @@
+/*
+ * $Id: joydump.c,v 1.1 2002/01/23 06:56:16 jsimmons Exp $
+ *
+ * Copyright (c) 1996-2001 Vojtech Pavlik
+ */
+
+/*
+ * This is just a very simple driver that can dump the data
+ * out of the joystick port into the syslog ...
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/gameport.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Gameport data dumper module");
+MODULE_LICENSE("GPL");
+
+#define BUF_SIZE 256
+
+struct joydump {
+ unsigned int time;
+ unsigned char data;
+};
+
+static void __devinit joydump_connect(struct gameport *gameport, struct gameport_dev *dev)
+{
+ struct joydump buf[BUF_SIZE];
+ int axes[4], buttons;
+ int i, j, t, timeout;
+ unsigned long flags;
+ unsigned char u;
+
+ printk(KERN_INFO "joydump: ,------------------- START ------------------.\n");
+ printk(KERN_INFO "joydump: | Dumping gameport%s.\n", gameport->phys);
+ printk(KERN_INFO "joydump: | Speed: %4d kHz. |\n", gameport->speed);
+
+ if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) {
+
+ printk(KERN_INFO "joydump: | Raw mode not available - trying cooked. |\n");
+
+ if (gameport_open(gameport, dev, GAMEPORT_MODE_COOKED)) {
+
+ printk(KERN_INFO "joydump: | Cooked not available either. Failing. |\n");
+ printk(KERN_INFO "joydump: `-------------------- END -------------------'\n");
+ return;
+ }
+
+ gameport_cooked_read(gameport, axes, &buttons);
+
+ for (i = 0; i < 4; i++)
+ printk(KERN_INFO "joydump: | Axis %d: %4d. |\n", i, axes[i]);
+ printk(KERN_INFO "joydump: | Buttons %02x. |\n", buttons);
+ printk(KERN_INFO "joydump: `-------------------- END -------------------'\n");
+ }
+
+ timeout = gameport_time(gameport, 10000); /* 10 ms */
+ t = 0;
+ i = 1;
+
+ save_flags(flags);
+ cli();
+
+ u = gameport_read(gameport);
+
+ buf[0].data = u;
+ buf[0].time = t;
+
+ gameport_trigger(gameport);
+
+ while (i < BUF_SIZE && t < timeout) {
+
+ buf[i].data = gameport_read(gameport);
+
+ if (buf[i].data ^ u) {
+ u = buf[i].data;
+ buf[i].time = t;
+ i++;
+ }
+ t++;
+ }
+
+ restore_flags(flags);
+
+/*
+ * Dump data.
+ */
+
+ t = i;
+
+ printk(KERN_INFO "joydump: >------------------- DATA -------------------<\n");
+ printk(KERN_INFO "joydump: | index: %3d delta: %3d.%02d us data: ", 0, 0, 0);
+ for (j = 7; j >= 0; j--)
+ printk("%d",(buf[0].data >> j) & 1);
+ printk(" |\n");
+ for (i = 1; i < t; i++) {
+ printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ",
+ i, buf[i].time - buf[i-1].time);
+ for (j = 7; j >= 0; j--)
+ printk("%d",(buf[i].data >> j) & 1);
+ printk(" |\n");
+ }
+
+ printk(KERN_INFO "joydump: `-------------------- END -------------------'\n");
+}
+
+static void __devexit joydump_disconnect(struct gameport *gameport)
+{
+ gameport_close(gameport);
+}
+
+static struct gameport_dev joydump_dev = {
+ connect: joydump_connect,
+ disconnect: joydump_disconnect,
+};
+
+static int __init joydump_init(void)
+{
+ gameport_register_device(&joydump_dev);
+ return 0;
+}
+
+static void __exit joydump_exit(void)
+{
+ gameport_unregister_device(&joydump_dev);
+}
+
+module_init(joydump_init);
+module_exit(joydump_exit);
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
index 3e6139a62979..2930248b443a 100644
--- a/drivers/input/joystick/magellan.c
+++ b/drivers/input/joystick/magellan.c
@@ -156,7 +156,7 @@ static void magellan_connect(struct serio *serio, struct serio_dev *dev)
magellan->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < 9; i++)
- set_bit(magellan_buttons[i], &magellan->dev.keybit);
+ set_bit(magellan_buttons[i], magellan->dev.keybit);
for (i = 0; i < 6; i++) {
t = magellan_axes[i];
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
new file mode 100644
index 000000000000..70da80e89404
--- /dev/null
+++ b/drivers/input/joystick/twidjoy.c
@@ -0,0 +1,264 @@
+/*
+ * $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $
+ *
+ * derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp"
+ *
+ * Copyright (c) 2001 Arndt Schoenewald
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ * Copyright (c) 2000 Mark Fletcher
+ *
+ * Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany
+ */
+
+/*
+ * Driver to use Handykey's Twiddler (the first edition, i.e. the one with
+ * the RS232 interface) as a joystick under Linux
+ *
+ * The Twiddler is a one-handed chording keyboard featuring twelve buttons on
+ * the front, six buttons on the top, and a built-in tilt sensor. The buttons
+ * on the front, which are grouped as four rows of three buttons, are pressed
+ * by the four fingers (this implies only one button per row can be held down
+ * at the same time) and the buttons on the top are for the thumb. The tilt
+ * sensor delivers X and Y axis data depending on how the Twiddler is held.
+ * Additional information can be found at http://www.handykey.com.
+ *
+ * This driver does not use the Twiddler for its intended purpose, i.e. as
+ * a chording keyboard, but as a joystick: pressing and releasing a button
+ * immediately sends a corresponding button event, and tilting it generates
+ * corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game
+ * controller with amazing 18 buttons :-)
+ *
+ * Note: The Twiddler2 (the successor of the Twiddler that connects directly
+ * to the PS/2 keyboard and mouse ports) is NOT supported by this driver!
+ *
+ * For questions or feedback regarding this driver module please contact:
+ * Arndt Schoenewald <arndt@quelltext.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+/*
+ * Constants.
+ */
+
+#define TWIDJOY_MAX_LENGTH 5
+
+static char *twidjoy_name = "Handykey Twiddler";
+
+static struct twidjoy_button_spec {
+ int bitshift;
+ int bitmask;
+ int buttons[3];
+}
+twidjoy_buttons[] = {
+ { 0, 3, { BTN_A, BTN_B, BTN_C } },
+ { 2, 3, { BTN_X, BTN_Y, BTN_Z } },
+ { 4, 3, { BTN_TL, BTN_TR, BTN_TR2 } },
+ { 6, 3, { BTN_SELECT, BTN_START, BTN_MODE } },
+ { 8, 1, { BTN_BASE5 } },
+ { 9, 1, { BTN_BASE } },
+ { 10, 1, { BTN_BASE3 } },
+ { 11, 1, { BTN_BASE4 } },
+ { 12, 1, { BTN_BASE2 } },
+ { 13, 1, { BTN_BASE6 } },
+ { 0, 0, { 0 } }
+};
+
+/*
+ * Per-Twiddler data.
+ */
+
+struct twidjoy {
+ struct input_dev dev;
+ int idx;
+ unsigned char data[TWIDJOY_MAX_LENGTH];
+ char phys[32];
+};
+
+/*
+ * twidjoy_process_packet() decodes packets the driver receives from the
+ * Twiddler. It updates the data accordingly.
+ */
+
+static void twidjoy_process_packet(struct twidjoy *twidjoy)
+{
+ if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
+ struct input_dev *dev = &twidjoy->dev;
+ unsigned char *data = twidjoy->data;
+ struct twidjoy_button_spec *bp;
+ int button_bits, abs_x, abs_y;
+
+ button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f);
+
+ for (bp = twidjoy_buttons; bp->bitmask; bp++) {
+ int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift;
+ int i;
+
+ for (i = 0; i < bp->bitmask; i++)
+ input_report_key(dev, bp->buttons[i], i+1 == value);
+ }
+
+ abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2);
+ if (data[4] & 0x08) abs_x -= 256;
+
+ abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0);
+ if (data[3] & 0x02) abs_y -= 256;
+
+ input_report_abs(dev, ABS_X, -abs_x);
+ input_report_abs(dev, ABS_Y, +abs_y);
+ }
+
+ return;
+}
+
+/*
+ * twidjoy_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static void twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct twidjoy *twidjoy = serio->private;
+
+ /* All Twiddler packets are 5 bytes. The fact that the first byte
+ * has a MSB of 0 and all other bytes have a MSB of 1 can be used
+ * to check and regain sync. */
+
+ if ((data & 0x80) == 0)
+ twidjoy->idx = 0; /* this byte starts a new packet */
+ else if (twidjoy->idx == 0)
+ return; /* wrong MSB -- ignore this byte */
+
+ if (twidjoy->idx < TWIDJOY_MAX_LENGTH)
+ twidjoy->data[twidjoy->idx++] = data;
+
+ if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
+ twidjoy_process_packet(twidjoy);
+ twidjoy->idx = 0;
+ }
+
+ return;
+}
+
+/*
+ * twidjoy_disconnect() is the opposite of twidjoy_connect()
+ */
+
+static void twidjoy_disconnect(struct serio *serio)
+{
+ struct twidjoy *twidjoy = serio->private;
+ input_unregister_device(&twidjoy->dev);
+ serio_close(serio);
+ kfree(twidjoy);
+}
+
+/*
+ * twidjoy_connect() is the routine that is called when someone adds a
+ * new serio device. It looks for the Twiddler, and if found, registers
+ * it as an input device.
+ */
+
+static void twidjoy_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct twidjoy_button_spec *bp;
+ struct twidjoy *twidjoy;
+ int i;
+
+ if (serio->type != (SERIO_RS232 | SERIO_TWIDJOY))
+ return;
+
+ if (!(twidjoy = kmalloc(sizeof(struct twidjoy), GFP_KERNEL)))
+ return;
+
+ memset(twidjoy, 0, sizeof(struct twidjoy));
+
+ sprintf(twidjoy->phys, "%s/input0", serio->phys);
+
+ twidjoy->dev.name = twidjoy_name;
+ twidjoy->dev.phys = twidjoy->phys;
+ twidjoy->dev.idbus = BUS_RS232;
+ twidjoy->dev.idvendor = SERIO_TWIDJOY;
+ twidjoy->dev.idproduct = 0x0001;
+ twidjoy->dev.idversion = 0x0100;
+
+ twidjoy->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (bp = twidjoy_buttons; bp->bitmask; bp++) {
+ for (i = 0; i < bp->bitmask; i++)
+ set_bit(bp->buttons[i], twidjoy->dev.keybit);
+ }
+
+ twidjoy->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+
+ for (i = 0; i < 2; i++) {
+ twidjoy->dev.absmax[ABS_X+i] = 50;
+ twidjoy->dev.absmin[ABS_X+i] = -50;
+
+ /* TODO: arndt 20010708: Are these values appropriate? */
+ twidjoy->dev.absfuzz[ABS_X+i] = 4;
+ twidjoy->dev.absflat[ABS_X+i] = 4;
+ }
+
+ twidjoy->dev.private = twidjoy;
+
+ serio->private = twidjoy;
+
+ if (serio_open(serio, dev)) {
+ kfree(twidjoy);
+ return;
+ }
+
+ input_register_device(&twidjoy->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", twidjoy_name, serio->phys);
+}
+
+/*
+ * The serio device structure.
+ */
+
+static struct serio_dev twidjoy_dev = {
+ interrupt: twidjoy_interrupt,
+ connect: twidjoy_connect,
+ disconnect: twidjoy_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+int __init twidjoy_init(void)
+{
+ serio_register_device(&twidjoy_dev);
+ return 0;
+}
+
+void __exit twidjoy_exit(void)
+{
+ serio_unregister_device(&twidjoy_dev);
+}
+
+module_init(twidjoy_init);
+module_exit(twidjoy_exit);
diff --git a/drivers/input/keybdev.c b/drivers/input/keybdev.c
index 6731dd2542c2..8a5a93b57f7a 100644
--- a/drivers/input/keybdev.c
+++ b/drivers/input/keybdev.c
@@ -1,5 +1,5 @@
/*
- * $Id: keybdev.c,v 1.16 2002/01/09 04:21:41 lethal Exp $
+ * $Id: keybdev.c,v 1.19 2002/03/13 10:09:20 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
@@ -179,7 +179,7 @@ void panic_blink(void)
static unsigned long last_jiffie;
static char led;
/* Roughly 1/2s frequency. KDB uses about 1s. Make sure it is different. */
- if (jiffies - last_jiffie > HZ/2) {
+ if (time_after(jiffies, last_jiffie + HZ/2)) {
led ^= 0x01 | 0x04;
keybdev_ledfunc(led);
last_jiffie = jiffies;
diff --git a/drivers/input/keyboard/Config.help b/drivers/input/keyboard/Config.help
new file mode 100644
index 000000000000..3d7df5a61a23
--- /dev/null
+++ b/drivers/input/keyboard/Config.help
@@ -0,0 +1,66 @@
+CONFIG_INPUT_KEYBOARD
+ Say Y here, and a list of supported keyboards will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+CONFIG_KEYBOARD_ATKBD
+ Say Y here if you want to use the standard AT keyboard. Usually
+ you'll need this, unless you have a different type keyboard (USB,
+ ADB or other).
+
+ If unsure, say Y.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called atkbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_KEYBOARD_SUNKBD
+ Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
+ connected either to the Sun keyboard connector or to an serial
+ (RS-232) port via a simple adapter.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called sunkbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_KEYBOARD_PS2SERKBD
+ Say Y here if you want to use a PS/2 to Serial converter with a
+ keyboard attached to it.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called ps2serkbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_KEYBOARD_XTKBD
+ Say Y here if you want to use the old IBM PC/XT keyboard (or
+ compatible) on your system. This is only possible with a
+ parallel port keyboard adapter, you cannot connect it to the
+ keyboard port on a PC that runs Linux.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called xtkbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_KEYBOARD_MAPLE
+ Say Y here if you have a DreamCast console running Linux and have
+ a keyboard attached to its Maple bus.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called maple_keyb.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_KEYBOARD_AMIGA
+ Say Y here if you are running Linux on any AMIGA and have a keyboard
+ attached.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called amikbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
diff --git a/drivers/input/keyboard/Config.in b/drivers/input/keyboard/Config.in
new file mode 100644
index 000000000000..f7f7128ee405
--- /dev/null
+++ b/drivers/input/keyboard/Config.in
@@ -0,0 +1,18 @@
+#
+# Input core configuration
+#
+
+bool 'Keyboards' CONFIG_INPUT_KEYBOARD
+
+dep_tristate ' AT keyboard support' CONFIG_KEYBOARD_ATKBD $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_SERIO
+dep_tristate ' Sun Type 4 and Type 5 keyboard support' CONFIG_KEYBOARD_SUNKBD $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_SERIO
+dep_tristate ' PS/2 to Serial converter support' CONFIG_KEYBOARD_PS2SERKBD $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_SERIO
+dep_tristate ' XT Keyboard support' CONFIG_KEYBOARD_XTKBD $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_SERIO
+
+if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then
+ dep_tristate ' Maple bus keyboard support' CONFIG_KEYBOARD_MAPLE $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_MAPLE
+fi
+
+if [ "$CONFIG_AMIGA" = "y" ]; then
+ dep_tristate ' Amiga keyboard' CONFIG_KEYBOARD_AMIGA $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD
+fi
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
new file mode 100644
index 000000000000..d51417534b60
--- /dev/null
+++ b/drivers/input/keyboard/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
+obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
+obj-$(CONFIG_KEYBOARD_PS2SERKBD) += ps2serkbd.o
+obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
+obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
new file mode 100644
index 000000000000..330b33615165
--- /dev/null
+++ b/drivers/input/keyboard/amikbd.c
@@ -0,0 +1,144 @@
+/*
+ * $Id: amikbd.c,v 1.13 2002/02/01 16:02:24 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Hamish Macdonald
+ */
+
+/*
+ * Amiga keyboard driver for Linux/m68k
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Amiga keyboard driver");
+MODULE_LICENSE("GPL");
+
+static unsigned char amikbd_keycode[0x78] = {
+ 41, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 43, 0, 82,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 0, 79, 80, 81,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 75, 76, 77,
+ 0, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 0, 83, 71, 72, 73,
+ 57, 14, 15, 96, 28, 1,111, 0, 0, 0, 74, 0,103,108,106,105,
+ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 98, 55, 78, 87,
+ 42, 54, 58, 29, 56,100
+}
+
+static char *amikbd_messages[] = {
+ KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n",
+ KERN_WARNING "amikbd: keyboard lost sync\n",
+ KERN_WARNING "amikbd: keyboard buffer overflow\n",
+ KERN_WARNING "amikbd: keyboard controller failure\n",
+ KERN_ERR "amikbd: keyboard selftest failure\n",
+ KERN_INFO "amikbd: initiate power-up key stream\n",
+ KERN_INFO "amikbd: terminate power-up key stream\n",
+ KERN_WARNING "amikbd: keyboard interrupt\n"
+};
+
+static struct input_dev amikbd_dev;
+
+static char *amikbd_name = "Amiga keyboard";
+static char *amikbd_phys = "amikbd/input0";
+
+static void amikbd_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+ unsigned char scancode, down;
+
+ scancode = ~ciaa.sdr; /* get and invert scancode (keyboard is active low) */
+ ciaa.cra |= 0x40; /* switch SP pin to output for handshake */
+ udelay(85); /* wait until 85 us have expired */
+ ciaa.cra &= ~0x40; /* switch CIA serial port to input mode */
+
+ down = scancode & 1; /* lowest bit is release bit */
+ scancode = scancode >> 1;
+
+ if (scancode < 0x78) { /* scancodes < 0x78 are keys */
+
+ scancode = amikbd_keycode[scancode];
+
+ if (scancode == KEY_CAPS) { /* CapsLock is a toggle switch key on Amiga */
+ input_report_key(&amikbd_dev, scancode, 1);
+ input_report_key(&amikbd_dev, scancode, 0);
+ return;
+ }
+
+ input_report_key(&amikbd_dev, scancode, down);
+
+ return;
+ }
+
+ printk(amikbd_messages[scancode - 0x78]); /* scancodes >= 0x78 are error codes */
+}
+
+static int __init amikbd_init(void)
+{
+ int i;
+
+ if (!AMIGAHW_PRESENT(AMI_KEYBOARD))
+ return -EIO;
+
+ if (!request_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100, "amikeyb"))
+ return -EBUSY;
+
+ amikbd_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ amikbd_dev.keycode = amikbd_keycode;
+
+ for (i = 0; i < 0x78; i++)
+ if (amikbd_keycode[i])
+ set_bit(amikbd_keycode[i], amikbd_dev.keybit);
+
+ ciaa.cra &= ~0x41; /* serial data in, turn off TA */
+ request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd", NULL);
+
+ amikbd_dev.name = amikbd_name;
+ amikbd_dev.phys = amikbd_phys;
+ amikbd_dev.idbus = BUS_AMIGA;
+ amikbd_dev.idvendor = 0x0001;
+ amikbd_dev.idproduct = 0x0001;
+ amikbd_dev.idversion = 0x0100;
+
+ input_register_device(&amikbd_dev);
+
+ printk(KERN_INFO "input: %s\n", amikbd_name);
+
+ return 0;
+}
+
+static void __exit amikbd_exit(void)
+{
+ input_unregister_device(&amikbd_dev);
+ free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt);
+ release_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100);
+}
+
+module_init(amikbd_init);
+module_exit(amikbd_exit);
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
new file mode 100644
index 000000000000..e14f581a4d67
--- /dev/null
+++ b/drivers/input/keyboard/atkbd.c
@@ -0,0 +1,565 @@
+/*
+ * $Id: atkbd.c,v 1.33 2002/02/12 09:34:34 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/tqueue.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
+MODULE_PARM(atkbd_set, "1i");
+MODULE_LICENSE("GPL");
+
+static int atkbd_set = 2;
+
+/*
+ * Scancode to keycode tables. These are just the default setting, and
+ * are loadable via an userland utility.
+ */
+
+static unsigned char atkbd_set2_keycode[512] = {
+ 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41, 85,
+ 0, 56, 42, 0, 29, 16, 2, 89, 0, 0, 44, 31, 30, 17, 3, 90,
+ 0, 46, 45, 32, 18, 5, 4, 91, 0, 57, 47, 33, 20, 19, 6, 0,
+ 0, 49, 48, 35, 34, 21, 7, 0, 0, 0, 50, 36, 22, 8, 9, 0,
+ 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
+ 122, 89, 40,120, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 0,
+ 85, 86, 90, 91, 92, 93, 14, 94, 95, 79, 0, 75, 71,121, 0,123,
+ 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+ 252, 0, 0, 65, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,251, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 252,253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
+ 0, 0, 92, 90, 85, 0,137, 0, 0, 0, 0, 91, 89,144,115, 0,
+ 136,100,255, 0, 97,149,164, 0,156, 0, 0,140,115, 0, 0,125,
+ 0,150, 0,154,152,163,151,126,112,166, 0,140, 0,147, 0,127,
+ 159,167,139,160,163, 0, 0,116,158, 0,150,165, 0, 0, 0,142,
+ 157, 0,114,166,168, 0, 0, 0,155, 0, 98,113, 0,148, 0,138,
+ 0, 0, 0, 0, 0, 0,153,140, 0,255, 96, 0, 0, 0,143, 0,
+ 133, 0,116, 0,143, 0,174,133, 0,107, 0,105,102, 0, 0,112,
+ 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119
+};
+
+static unsigned char atkbd_set3_keycode[512] = {
+ 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60,
+ 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62,
+ 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64,
+ 136, 49, 48, 35, 34, 21, 7, 65,137,100, 50, 36, 22, 8, 9, 66,
+ 125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68,
+ 113,114, 40, 84, 26, 13, 87, 99,100, 54, 28, 27, 43, 84, 88, 70,
+ 108,105,119,103,111,107, 14,110, 0, 79,106, 75, 71,109,102,104,
+ 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55, 85,
+ 89, 90, 91, 92, 74, 0, 0, 0, 0, 0, 0,125,126,127,112, 0,
+ 0,139,150,163,165,115,152,150,166,140,160,154,113,114,167,168,
+ 148,149,147,140, 0, 0, 0, 0, 0, 0,251, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 252,253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255
+};
+
+#define ATKBD_CMD_SETLEDS 0x10ed
+#define ATKBD_CMD_GSCANSET 0x11f0
+#define ATKBD_CMD_SSCANSET 0x10f0
+#define ATKBD_CMD_GETID 0x02f2
+#define ATKBD_CMD_ENABLE 0x00f4
+#define ATKBD_CMD_RESET_DIS 0x00f5
+#define ATKBD_CMD_SETALL_MB 0x00f8
+#define ATKBD_CMD_EX_ENABLE 0x10ea
+#define ATKBD_CMD_EX_SETLEDS 0x20eb
+
+#define ATKBD_RET_ACK 0xfa
+#define ATKBD_RET_NAK 0xfe
+
+#define ATKBD_KEY_UNKNOWN 0
+#define ATKBD_KEY_BAT 251
+#define ATKBD_KEY_EMUL0 252
+#define ATKBD_KEY_EMUL1 253
+#define ATKBD_KEY_RELEASE 254
+#define ATKBD_KEY_NULL 255
+
+/*
+ * The atkbd control structure
+ */
+
+struct atkbd {
+ unsigned char keycode[512];
+ struct input_dev dev;
+ struct serio *serio;
+ char name[64];
+ char phys[32];
+ struct tq_struct tq;
+ unsigned char cmdbuf[4];
+ unsigned char cmdcnt;
+ unsigned char set;
+ char release;
+ char ack;
+ char emul;
+ char error;
+ unsigned short id;
+};
+
+/*
+ * atkbd_interrupt(). Here takes place processing of data received from
+ * the keyboard into events.
+ */
+
+static void atkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct atkbd *atkbd = serio->private;
+ int code = data;
+
+#ifdef ATKBD_DEBUG
+ printk(KERN_DEBUG "atkbd.c: Received %02x\n", data);
+#endif
+
+ switch (code) {
+ case ATKBD_RET_ACK:
+ atkbd->ack = 1;
+ return;
+ case ATKBD_RET_NAK:
+ atkbd->ack = -1;
+ return;
+ }
+
+ if (atkbd->cmdcnt) {
+ atkbd->cmdbuf[--atkbd->cmdcnt] = code;
+ return;
+ }
+
+ switch (atkbd->keycode[code]) {
+ case ATKBD_KEY_BAT:
+ queue_task(&atkbd->tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ return;
+ case ATKBD_KEY_EMUL0:
+ atkbd->emul = 1;
+ return;
+ case ATKBD_KEY_EMUL1:
+ atkbd->emul = 2;
+ return;
+ case ATKBD_KEY_RELEASE:
+ atkbd->release = 1;
+ return;
+ }
+
+ if (atkbd->emul) {
+ if (--atkbd->emul) return;
+ code |= 0x100;
+ }
+
+ switch (atkbd->keycode[code]) {
+ case ATKBD_KEY_NULL:
+ break;
+ case ATKBD_KEY_UNKNOWN:
+ printk(KERN_WARNING "atkbd.c: Unknown key (set %d, scancode %#x, on %s) %s.\n",
+ atkbd->set, code, serio->phys, atkbd->release ? "released" : "pressed");
+ break;
+ default:
+ input_report_key(&atkbd->dev, atkbd->keycode[code], !atkbd->release);
+ }
+
+ atkbd->release = 0;
+}
+
+/*
+ * atkbd_sendbyte() sends a byte to the keyboard, and waits for
+ * acknowledge. It doesn't handle resends according to the keyboard
+ * protocol specs, because if these are needed, the keyboard needs
+ * replacement anyway, and they only make a mess in the protocol.
+ */
+
+static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
+{
+ int timeout = 10000; /* 100 msec */
+ atkbd->ack = 0;
+
+#ifdef ATKBD_DEBUG
+ printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
+#endif
+ serio_write(atkbd->serio, byte);
+ while (!atkbd->ack && timeout--) udelay(10);
+
+ return -(atkbd->ack <= 0);
+}
+
+/*
+ * atkbd_command() sends a command, and its parameters to the keyboard,
+ * then waits for the response and puts it in the param array.
+ */
+
+static int atkbd_command(struct atkbd *atkbd, unsigned char *param, int command)
+{
+ int timeout = 50000; /* 500 msec */
+ int send = (command >> 12) & 0xf;
+ int receive = (command >> 8) & 0xf;
+ int i;
+
+ atkbd->cmdcnt = receive;
+
+ if (command & 0xff)
+ if (atkbd_sendbyte(atkbd, command & 0xff))
+ return (atkbd->cmdcnt = 0) - 1;
+
+ for (i = 0; i < send; i++)
+ if (atkbd_sendbyte(atkbd, param[i]))
+ return (atkbd->cmdcnt = 0) - 1;
+
+ while (atkbd->cmdcnt && timeout--) udelay(10);
+
+ for (i = 0; i < receive; i++)
+ param[i] = atkbd->cmdbuf[(receive - 1) - i];
+
+ if (atkbd->cmdcnt)
+ return (atkbd->cmdcnt = 0) - 1;
+
+ return 0;
+}
+
+/*
+ * Event callback from the input module. Events that change the state of
+ * the hardware are processed here.
+ */
+
+static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct atkbd *atkbd = dev->private;
+ char param[2];
+
+ if (!atkbd->serio->write)
+ return -1;
+
+ switch (type) {
+
+ case EV_LED:
+
+ *param = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
+ | (test_bit(LED_NUML, dev->led) ? 2 : 0)
+ | (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
+ atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS);
+
+ if (atkbd->set == 4) {
+ param[0] = 0;
+ param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
+ | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0)
+ | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
+ | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
+ atkbd_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * atkbd_set_3 checks if a keyboard has a working Set 3 support, and
+ * sets it into that. Unfortunately there are keyboards that can be switched
+ * to Set 3, but don't work well in that (BTC Multimedia ...)
+ */
+
+static int atkbd_set_3(struct atkbd *atkbd)
+{
+ unsigned char param;
+
+/*
+ * For known special keyboards we can go ahead and set the correct set.
+ */
+
+ if (atkbd->id == 0xaca1) {
+ param = 3;
+ atkbd_command(atkbd, &param, ATKBD_CMD_SSCANSET);
+ return 3;
+ }
+
+/*
+ * We check for the extra keys on an some keyboards that need extra
+ * command to get enabled. This shouldn't harm any keyboards not
+ * knowing the command.
+ */
+
+ param = 0x71;
+ if (!atkbd_command(atkbd, &param, ATKBD_CMD_EX_ENABLE))
+ return 4;
+
+/*
+ * Try to set the set we want.
+ */
+
+ param = atkbd_set;
+ if (atkbd_command(atkbd, &param, ATKBD_CMD_SSCANSET))
+ return 2;
+
+/*
+ * Read set number. Beware here. Some keyboards always send '2'
+ * or some other number regardless into what mode they have been
+ * attempted to be set. Other keyboards treat the '0' command as
+ * 'set to set 0', and not 'report current set' as they should.
+ * In that case we time out, and return 2.
+ */
+
+ param = 0;
+ if (atkbd_command(atkbd, &param, ATKBD_CMD_GSCANSET))
+ return 2;
+
+/*
+ * Here we return the set number the keyboard reports about
+ * itself.
+ */
+
+ return (param == 3) ? 3 : 2;
+}
+
+/*
+ * atkbd_probe() probes for an AT keyboard on a serio port.
+ */
+
+static int atkbd_probe(struct atkbd *atkbd)
+{
+ unsigned char param[2];
+
+/*
+ * Full reset with selftest can on some keyboards be annoyingly slow,
+ * so we just do a reset-and-disable on the keyboard, which
+ * is considerably faster, but doesn't have to reset everything.
+ */
+
+ if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_DIS))
+ return -1;
+
+/*
+ * Next, we check if it's a keyboard. It should send 0xab83
+ * (0xab84 on IBM ThinkPad, and 0xaca1 on a NCD Sun layout keyboard,
+ * 0xab02 on unxlated i8042 and 0xab03 on unxlated ThinkPad, 0xab7f
+ * on Fujitsu Lifebook).
+ * If it's a mouse, it'll only send 0x00 (0x03 if it's MS mouse),
+ * and we'll time out here, and report an error.
+ */
+
+ param[0] = param[1] = 0;
+
+ if (atkbd_command(atkbd, param, ATKBD_CMD_GETID))
+ return -1;
+
+ atkbd->id = (param[0] << 8) | param[1];
+
+ if (atkbd->id != 0xab83 && atkbd->id != 0xab84 && atkbd->id != 0xaca1 &&
+ atkbd->id != 0xab7f && atkbd->id != 0xab02 && atkbd->id != 0xab03)
+ printk(KERN_WARNING "atkbd.c: Unusual keyboard ID: %#x on %s\n",
+ atkbd->id, atkbd->serio->phys);
+
+ return 0;
+}
+
+/*
+ * atkbd_initialize() sets the keyboard into a sane state.
+ */
+
+static void atkbd_initialize(struct atkbd *atkbd)
+{
+ unsigned char param;
+
+/*
+ * Disable autorepeat. We don't need it, as we do it in software anyway,
+ * because that way can get faster repeat, and have less system load
+ * (less accesses to the slow ISA hardware). If this fails, we don't care,
+ * and will just ignore the repeated keys.
+ */
+
+ atkbd_command(atkbd, NULL, ATKBD_CMD_SETALL_MB);
+
+/*
+ * We also shut off all the leds. The console code will turn them back on,
+ * if needed.
+ */
+
+ param = 0;
+ atkbd_command(atkbd, &param, ATKBD_CMD_SETLEDS);
+
+/*
+ * Last, we enable the keyboard so that we get keypresses from it.
+ */
+
+ if (atkbd_command(atkbd, NULL, ATKBD_CMD_ENABLE))
+ printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n",
+ atkbd->serio->phys);
+}
+
+/*
+ * atkbd_disconnect() cleans up behind us ...
+ */
+
+static void atkbd_disconnect(struct serio *serio)
+{
+ struct atkbd *atkbd = serio->private;
+ input_unregister_device(&atkbd->dev);
+ serio_close(serio);
+ kfree(atkbd);
+}
+
+/*
+ * atkbd_powerup() is called when the keyboard sends the 0xaa character,
+ * meaning that it was disconnected and reconnected. We close the port
+ * in that case and let the upper layer find an appropriate driver for
+ * the device that was connected. It may be a mouse, or a keyboard, we
+ * don't know yet.
+ */
+
+static void atkbd_powerup(void *data)
+{
+ struct atkbd *atkbd = data;
+ mdelay(40); /* FIXME!!! Wait some nicer way */
+ serio_rescan(atkbd->serio);
+}
+
+/*
+ * atkbd_connect() is called when the serio module finds and interface
+ * that isn't handled yet by an appropriate device driver. We check if
+ * there is an AT keyboard out there and if yes, we register ourselves
+ * to the input module.
+ */
+
+static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct atkbd *atkbd;
+ int i;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_8042)
+ return;
+
+ if (!(atkbd = kmalloc(sizeof(struct atkbd), GFP_KERNEL)))
+ return;
+
+ memset(atkbd, 0, sizeof(struct atkbd));
+
+ if (serio->write) {
+ atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
+ atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ } else atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ atkbd->serio = serio;
+
+ atkbd->dev.keycode = atkbd->keycode;
+ atkbd->dev.event = atkbd_event;
+ atkbd->dev.private = atkbd;
+
+ atkbd->tq.routine = atkbd_powerup;
+ atkbd->tq.data = atkbd;
+
+ serio->private = atkbd;
+
+ if (serio_open(serio, dev)) {
+ kfree(atkbd);
+ return;
+ }
+
+ if (serio->write) {
+
+ if (atkbd_probe(atkbd)) {
+ serio_close(serio);
+ kfree(atkbd);
+ return;
+ }
+
+ atkbd->set = atkbd_set_3(atkbd);
+
+ } else {
+ atkbd->set = 2;
+ atkbd->id = 0xab00;
+ }
+
+ if (atkbd->set == 4) {
+ atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) | BIT(LED_SLEEP) | BIT(LED_MUTE);
+ sprintf(atkbd->name, "AT Set 2 Extended keyboard\n");
+ } else
+ sprintf(atkbd->name, "AT Set %d keyboard", atkbd->set);
+
+ sprintf(atkbd->phys, "%s/input0", serio->phys);
+
+ if (atkbd->set == 3)
+ memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode));
+ else
+ memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));
+
+ atkbd->dev.name = atkbd->name;
+ atkbd->dev.phys = atkbd->phys;
+ atkbd->dev.idbus = BUS_I8042;
+ atkbd->dev.idvendor = 0x0001;
+ atkbd->dev.idproduct = atkbd->set;
+ atkbd->dev.idversion = atkbd->id;
+
+ for (i = 0; i < 512; i++)
+ if (atkbd->keycode[i] && atkbd->keycode[i] <= 250)
+ set_bit(atkbd->keycode[i], atkbd->dev.keybit);
+
+ input_register_device(&atkbd->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys);
+
+ if (serio->write)
+ atkbd_initialize(atkbd);
+}
+
+
+static struct serio_dev atkbd_dev = {
+ interrupt: atkbd_interrupt,
+ connect: atkbd_connect,
+ disconnect: atkbd_disconnect
+};
+
+/*
+ * Module init and exit.
+ */
+
+void __init atkbd_setup(char *str, int *ints)
+{
+ if (!ints[0]) atkbd_set = ints[1];
+}
+
+int __init atkbd_init(void)
+{
+ serio_register_device(&atkbd_dev);
+ return 0;
+}
+
+void __exit atkbd_exit(void)
+{
+ serio_unregister_device(&atkbd_dev);
+}
+
+module_init(atkbd_init);
+module_exit(atkbd_exit);
diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
new file mode 100644
index 000000000000..feaea91dc8b4
--- /dev/null
+++ b/drivers/input/keyboard/maple_keyb.c
@@ -0,0 +1,190 @@
+/*
+ * $Id: maple_keyb.c,v 1.1 2001/11/02 17:27:32 jsimmons Exp $
+ * SEGA Dreamcast keyboard driver
+ * Based on drivers/usb/usbkbd.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("YAEGASHI Takeshi <t@keshi.org>");
+MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
+
+static unsigned char dc_kbd_keycode[256] = {
+ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
+ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
+ 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
+ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
+ 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95,
+ 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113,
+ 115,114, 0, 0, 0,124, 0,181,182,183,184,185,186,187,188,189,
+ 190,191,192,193,194,195,196,197,198, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
+ 150,158,159,128,136,177,178,176,142,152,173,140
+};
+
+
+struct dc_kbd {
+ struct input_dev dev;
+ unsigned char new[8];
+ unsigned char old[8];
+ int open;
+};
+
+
+static void dc_scan_kbd(struct dc_kbd *kbd)
+{
+ int i;
+ struct input_dev *dev = &kbd->dev;
+
+ for(i=0; i<8; i++)
+ input_report_key(dev,
+ dc_kbd_keycode[i+224],
+ (kbd->new[0]>>i)&1);
+
+ for(i=2; i<8; i++) {
+
+ if(kbd->old[i]>3&&memscan(kbd->new+2, kbd->old[i], 6)==NULL) {
+ if(dc_kbd_keycode[kbd->old[i]])
+ input_report_key(dev,
+ dc_kbd_keycode[kbd->old[i]],
+ 0);
+ else
+ printk("Unknown key (scancode %#x) released.",
+ kbd->old[i]);
+ }
+
+ if(kbd->new[i]>3&&memscan(kbd->old+2, kbd->new[i], 6)!=NULL) {
+ if(dc_kbd_keycode[kbd->new[i]])
+ input_report_key(dev,
+ dc_kbd_keycode[kbd->new[i]],
+ 1);
+ else
+ printk("Unknown key (scancode %#x) pressed.",
+ kbd->new[i]);
+ }
+ }
+
+ memcpy(kbd->old, kbd->new, 8);
+}
+
+
+static void dc_kbd_callback(struct mapleq *mq)
+{
+ struct maple_device *mapledev = mq->dev;
+ struct dc_kbd *kbd = mapledev->private_data;
+ unsigned long *buf = mq->recvbuf;
+
+ if (buf[1] == mapledev->function) {
+ memcpy(kbd->new, buf+2, 8);
+ dc_scan_kbd(kbd);
+ }
+}
+
+
+static int dc_kbd_open(struct input_dev *dev)
+{
+ struct dc_kbd *kbd = dev->private;
+ kbd->open++;
+ return 0;
+}
+
+
+static void dc_kbd_close(struct input_dev *dev)
+{
+ struct dc_kbd *kbd = dev->private;
+ kbd->open--;
+}
+
+
+static int dc_kbd_connect(struct maple_device *dev)
+{
+ int i;
+ unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]);
+ struct dc_kbd *kbd;
+
+ if (!(kbd = kmalloc(sizeof(struct dc_kbd), GFP_KERNEL)))
+ return -1;
+ memset(kbd, 0, sizeof(struct dc_kbd));
+
+ dev->private_data = kbd;
+
+ kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ for (i=0; i<255; i++)
+ set_bit(dc_kbd_keycode[i], kbd->dev.keybit);
+ clear_bit(0, kbd->dev.keybit);
+
+ kbd->dev.private = kbd;
+ kbd->dev.open = dc_kbd_open;
+ kbd->dev.close = dc_kbd_close;
+ kbd->dev.event = NULL;
+
+ kbd->dev.name = dev->product_name;
+ kbd->dev.idbus = BUS_MAPLE;
+
+ input_register_device(&kbd->dev);
+
+ maple_getcond_callback(dev, dc_kbd_callback, 1, MAPLE_FUNC_KEYBOARD);
+
+ printk(KERN_INFO "input%d: keyboard(0x%lx): %s\n",
+ kbd->dev.number, data, kbd->dev.name);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+
+static void dc_kbd_disconnect(struct maple_device *dev)
+{
+ struct dc_kbd *kbd = dev->private_data;
+
+ input_unregister_device(&kbd->dev);
+
+ kfree(kbd);
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct maple_driver dc_kbd_driver = {
+ function: MAPLE_FUNC_KEYBOARD,
+ name: "Dreamcast keyboard",
+ connect: dc_kbd_connect,
+ disconnect: dc_kbd_disconnect,
+};
+
+
+static int __init dc_kbd_init(void)
+{
+ maple_register_driver(&dc_kbd_driver);
+ return 0;
+}
+
+
+static void __exit dc_kbd_exit(void)
+{
+ maple_unregister_driver(&dc_kbd_driver);
+}
+
+
+module_init(dc_kbd_init);
+module_exit(dc_kbd_exit);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/keyboard/ps2serkbd.c b/drivers/input/keyboard/ps2serkbd.c
new file mode 100644
index 000000000000..afa4c107ff4e
--- /dev/null
+++ b/drivers/input/keyboard/ps2serkbd.c
@@ -0,0 +1,298 @@
+/*
+ * based on: sunkbd.c and ps2serkbd.c
+ *
+ * $Id: ps2serkbd.c,v 1.5 2001/09/25 10:12:07 vojtech Exp $
+ */
+
+/*
+ * PS/2 keyboard via adapter at serial port driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/tqueue.h>
+
+#define ATKBD_CMD_SETLEDS 0x10ed
+#define ATKBD_CMD_GSCANSET 0x11f0
+#define ATKBD_CMD_SSCANSET 0x10f0
+#define ATKBD_CMD_GETID 0x02f2
+#define ATKBD_CMD_ENABLE 0x00f4
+#define ATKBD_CMD_RESET_DIS 0x00f5
+#define ATKBD_CMD_SETALL_MB 0x00f8
+#define ATKBD_CMD_EX_ENABLE 0x10ea
+#define ATKBD_CMD_EX_SETLEDS 0x20eb
+
+#define ATKBD_RET_ACK 0xfa
+#define ATKBD_RET_NAK 0xfe
+
+#define ATKBD_KEY_UNKNOWN 0
+#define ATKBD_KEY_BAT 251
+#define ATKBD_KEY_EMUL0 252
+#define ATKBD_KEY_EMUL1 253
+#define ATKBD_KEY_RELEASE 254
+#define ATKBD_KEY_NULL 255
+
+static unsigned char ps2serkbd_set2_keycode[512] = {
+ 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41, 85,
+ 0, 56, 42, 0, 29, 16, 2, 89, 0, 0, 44, 31, 30, 17, 3, 90,
+ 0, 46, 45, 32, 18, 5, 4, 91, 0, 57, 47, 33, 20, 19, 6, 0,
+ 0, 49, 48, 35, 34, 21, 7, 0, 0, 0, 50, 36, 22, 8, 9, 0,
+ 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
+ 122, 89, 40,120, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 0,
+ 85, 86, 90, 91, 92, 93, 14, 94, 95, 79, 0, 75, 71,121, 0,123,
+ 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+ 252, 0, 0, 65, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,251, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 252,253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
+ 0, 0, 92, 90, 85, 0,137, 0, 0, 0, 0, 91, 89,144,115, 0,
+ 136,100,255, 0, 97,149,164, 0,156, 0, 0,140,115, 0, 0,125,
+ 0,150, 0,154,152,163,151,126,112,166, 0,140, 0,147, 0,127,
+ 159,167,139,160,163, 0, 0,116,158, 0,150,165, 0, 0, 0,142,
+ 157, 0,114,166,168, 0, 0, 0,155, 0, 98,113, 0,148, 0,138,
+ 0, 0, 0, 0, 0, 0,153,140, 0, 0, 96, 0, 0, 0,143, 0,
+ 133, 0,116, 0,143, 0,174,133, 0,107, 0,105,102, 0, 0,112,
+ 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119
+};
+
+/*
+ * Per-keyboard data.
+ */
+
+struct ps2serkbd {
+ unsigned char keycode[512];
+ struct input_dev dev;
+ struct serio *serio;
+ char name[64];
+ char phys[32];
+ struct tq_struct tq;
+ unsigned char cmdbuf[4];
+ unsigned char cmdcnt;
+ unsigned char set;
+ char release;
+ char ack;
+ char emul;
+ char error;
+ unsigned short id;
+};
+
+/*
+ * ps2serkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+
+static void ps2serkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ static int event_count=0;
+ struct ps2serkbd* ps2serkbd = serio->private;
+ int code=data;
+
+#if 0
+ printk(KERN_WARNING "ps2serkbd.c(%8d): (scancode %#x)\n", event_count, data);
+#endif
+ event_count++;
+
+ switch (code) {
+ case ATKBD_RET_ACK:
+ ps2serkbd->ack = 1;
+ return;
+ case ATKBD_RET_NAK:
+ ps2serkbd->ack = -1;
+ return;
+ }
+
+ if (ps2serkbd->cmdcnt) {
+ ps2serkbd->cmdbuf[--ps2serkbd->cmdcnt] = code;
+ return;
+ }
+
+ switch (ps2serkbd->keycode[code]) {
+ case ATKBD_KEY_BAT:
+ queue_task(&ps2serkbd->tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ return;
+ case ATKBD_KEY_EMUL0:
+ ps2serkbd->emul = 1;
+ return;
+ case ATKBD_KEY_EMUL1:
+ ps2serkbd->emul = 2;
+ return;
+ case ATKBD_KEY_RELEASE:
+ ps2serkbd->release = 1;
+ return;
+ }
+
+ if (ps2serkbd->emul) {
+ if (--ps2serkbd->emul) return;
+ code |= 0x100;
+ }
+
+ switch (ps2serkbd->keycode[code]) {
+ case ATKBD_KEY_NULL:
+ break;
+ case ATKBD_KEY_UNKNOWN:
+ printk(KERN_WARNING "ps2serkbd.c: Unknown key (set %d, scancode %#x) %s.\n",
+ ps2serkbd->set, code, ps2serkbd->release ? "released" : "pressed");
+ break;
+ default:
+ input_report_key(&ps2serkbd->dev, ps2serkbd->keycode[code], !ps2serkbd->release);
+ }
+
+ ps2serkbd->release = 0;
+}
+
+/*
+ * ps2serkbd_event() handles events from the input module.
+ */
+
+static int ps2serkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ switch (type) {
+
+ case EV_LED:
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int ps2serkbd_initialize(struct ps2serkbd *ps2serkbd)
+{
+ return 0;
+}
+
+static void ps2serkbd_reinit(void *data)
+{
+}
+
+
+static void ps2serkbd_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct ps2serkbd *ps2serkbd;
+ int i;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_RS232)
+ return;
+
+ if ((serio->type & SERIO_PROTO) && (serio->type & SERIO_PROTO) != SERIO_PS2SER)
+ return;
+
+
+ if (!(ps2serkbd = kmalloc(sizeof(struct ps2serkbd), GFP_KERNEL)))
+ return;
+
+ memset(ps2serkbd, 0, sizeof(struct ps2serkbd));
+
+ ps2serkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
+ ps2serkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+
+ ps2serkbd->serio = serio;
+
+ ps2serkbd->dev.keycode = ps2serkbd->keycode;
+ ps2serkbd->dev.event = ps2serkbd_event;
+ ps2serkbd->dev.private = ps2serkbd;
+
+ ps2serkbd->tq.routine = ps2serkbd_reinit;
+ ps2serkbd->tq.data = ps2serkbd;
+
+ serio->private = ps2serkbd;
+
+ if (serio_open(serio, dev)) {
+ kfree(ps2serkbd);
+ return;
+ }
+
+ if (ps2serkbd_initialize(ps2serkbd) < 0) {
+ serio_close(serio);
+ kfree(ps2serkbd);
+ return;
+ }
+
+ ps2serkbd->set = 4;
+
+ if (ps2serkbd->set == 4) {
+ ps2serkbd->dev.ledbit[0] |= 0;
+ sprintf(ps2serkbd->name, "AT Set 2 Extended keyboard\n");
+ }
+ memcpy(ps2serkbd->keycode, ps2serkbd_set2_keycode, sizeof(ps2serkbd->keycode));
+
+ sprintf(ps2serkbd->phys, "%s/input0", serio->phys);
+
+ ps2serkbd->dev.name = ps2serkbd->name;
+ ps2serkbd->dev.phys = ps2serkbd->phys;
+ ps2serkbd->dev.idbus = BUS_RS232;
+ ps2serkbd->dev.idvendor = SERIO_PS2SER;
+ ps2serkbd->dev.idproduct = ps2serkbd->set;
+ ps2serkbd->dev.idversion = ps2serkbd->id;
+
+ for (i = 0; i < 512; i++)
+ if (ps2serkbd->keycode[i] && ps2serkbd->keycode[i] <= 250)
+ set_bit(ps2serkbd->keycode[i], ps2serkbd->dev.keybit);
+
+ input_register_device(&ps2serkbd->dev);
+}
+
+/*
+ * ps2serkbd_disconnect() unregisters and closes behind us.
+ */
+
+static void ps2serkbd_disconnect(struct serio *serio)
+{
+ struct ps2serkbd *ps2serkbd = serio->private;
+ input_unregister_device(&ps2serkbd->dev);
+ serio_close(serio);
+ kfree(ps2serkbd);
+}
+
+static struct serio_dev ps2serkbd_dev = {
+interrupt:
+ ps2serkbd_interrupt,
+connect:
+ ps2serkbd_connect,
+disconnect:
+ ps2serkbd_disconnect
+};
+
+/*
+ * The functions for insering/removing us as a module.
+ */
+
+int __init ps2serkbd_init(void)
+{
+ serio_register_device(&ps2serkbd_dev);
+ return 0;
+}
+
+void __exit ps2serkbd_exit(void)
+{
+ serio_unregister_device(&ps2serkbd_dev);
+}
+
+module_init(ps2serkbd_init);
+module_exit(ps2serkbd_exit);
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
new file mode 100644
index 000000000000..901567c2b355
--- /dev/null
+++ b/drivers/input/keyboard/sunkbd.c
@@ -0,0 +1,318 @@
+/*
+ * $Id: sunkbd.c,v 1.14 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Sun keyboard driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/tqueue.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Sun keyboard driver");
+MODULE_LICENSE("GPL");
+
+static unsigned char sunkbd_keycode[128] = {
+ 0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64, 0,
+ 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
+ 116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
+ 104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
+ 79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
+};
+
+#define SUNKBD_CMD_RESET 0x1
+#define SUNKBD_CMD_BELLON 0x2
+#define SUNKBD_CMD_BELLOFF 0x3
+#define SUNKBD_CMD_CLICK 0xa
+#define SUNKBD_CMD_NOCLICK 0xb
+#define SUNKBD_CMD_SETLED 0xe
+#define SUNKBD_CMD_LAYOUT 0xf
+
+#define SUNKBD_RET_RESET 0xff
+#define SUNKBD_RET_ALLUP 0x7f
+#define SUNKBD_RET_LAYOUT 0xfe
+
+#define SUNKBD_LAYOUT_5_MASK 0x20
+#define SUNKBD_RELEASE 0x80
+#define SUNKBD_KEY 0x7f
+
+/*
+ * Per-keyboard data.
+ */
+
+struct sunkbd {
+ unsigned char keycode[128];
+ struct input_dev dev;
+ struct serio *serio;
+ struct tq_struct tq;
+ char name[64];
+ char phys[32];
+ char type;
+ char reset;
+ char layout;
+};
+
+/*
+ * sunkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+
+static void sunkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct sunkbd* sunkbd = serio->private;
+
+ if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
+ sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */
+ return;
+ }
+
+ if (sunkbd->layout == -1) {
+ sunkbd->layout = data;
+ return;
+ }
+
+ switch (data) {
+
+ case SUNKBD_RET_RESET:
+ queue_task(&sunkbd->tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ sunkbd->reset = -1;
+ return;
+
+ case SUNKBD_RET_LAYOUT:
+ sunkbd->layout = -1;
+ return;
+
+ case SUNKBD_RET_ALLUP: /* All keys released */
+ return;
+
+ default:
+ if (sunkbd->keycode[data & SUNKBD_KEY]) {
+ input_report_key(&sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
+ } else {
+ printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
+ data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
+ }
+ }
+}
+
+/*
+ * sunkbd_event() handles events from the input module.
+ */
+
+static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct sunkbd *sunkbd = dev->private;
+
+ switch (type) {
+
+ case EV_LED:
+
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
+ sunkbd->serio->write(sunkbd->serio,
+ (!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
+ (!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
+ return 0;
+
+ case EV_SND:
+
+ switch (code) {
+
+ case SND_CLICK:
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
+ return 0;
+
+ case SND_BELL:
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
+ return 0;
+ }
+
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * sunkbd_initialize() checks for a Sun keyboard attached, and determines
+ * its type.
+ */
+
+static int sunkbd_initialize(struct sunkbd *sunkbd)
+{
+ int t;
+
+ t = 1000;
+ sunkbd->reset = -2;
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
+ while (sunkbd->reset < 0 && --t) mdelay(1);
+ if (!t) return -1;
+
+ sunkbd->type = sunkbd->reset;
+
+ if (sunkbd->type == 4) { /* Type 4 keyboard */
+ t = 250;
+ sunkbd->layout = -2;
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
+ while (sunkbd->layout < 0 && --t) mdelay(1);
+ if (!t) return -1;
+ if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
+ }
+
+ return 0;
+}
+
+/*
+ * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
+ * were in.
+ */
+
+static void sunkbd_reinit(void *data)
+{
+ struct sunkbd *sunkbd = data;
+ int t = 1000;
+
+ while (sunkbd->reset < 0 && --t) mdelay(1);
+
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
+ sunkbd->serio->write(sunkbd->serio,
+ (!!test_bit(LED_CAPSL, sunkbd->dev.led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev.led) << 2) |
+ (!!test_bit(LED_COMPOSE, sunkbd->dev.led) << 1) | !!test_bit(LED_NUML, sunkbd->dev.led));
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev.snd));
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev.snd));
+}
+
+/*
+ * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
+ */
+
+static void sunkbd_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct sunkbd *sunkbd;
+ int i;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_RS232)
+ return;
+
+ if ((serio->type & SERIO_PROTO) && (serio->type & SERIO_PROTO) != SERIO_SUNKBD)
+ return;
+
+ if (!(sunkbd = kmalloc(sizeof(struct sunkbd), GFP_KERNEL)))
+ return;
+
+ memset(sunkbd, 0, sizeof(struct sunkbd));
+
+ sunkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
+ sunkbd->dev.ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML);
+ sunkbd->dev.sndbit[0] = BIT(SND_CLICK) | BIT(SND_BELL);
+
+ sunkbd->serio = serio;
+
+ sunkbd->tq.routine = sunkbd_reinit;
+ sunkbd->tq.data = sunkbd;
+
+ sunkbd->dev.keycode = sunkbd->keycode;
+ sunkbd->dev.event = sunkbd_event;
+ sunkbd->dev.private = sunkbd;
+
+ serio->private = sunkbd;
+
+ if (serio_open(serio, dev)) {
+ kfree(sunkbd);
+ return;
+ }
+
+ if (sunkbd_initialize(sunkbd) < 0) {
+ serio_close(serio);
+ kfree(sunkbd);
+ return;
+ }
+
+ sprintf(sunkbd->name, "Sun Type %d keyboard", sunkbd->type);
+
+ memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
+ for (i = 0; i < 255; i++)
+ set_bit(sunkbd->keycode[i], sunkbd->dev.keybit);
+ clear_bit(0, sunkbd->dev.keybit);
+
+ sprintf(sunkbd->name, "%s/input", serio->phys);
+
+ sunkbd->dev.name = sunkbd->name;
+ sunkbd->dev.phys = sunkbd->phys;
+ sunkbd->dev.idbus = BUS_RS232;
+ sunkbd->dev.idvendor = SERIO_SUNKBD;
+ sunkbd->dev.idproduct = sunkbd->type;
+ sunkbd->dev.idversion = 0x0100;
+
+ input_register_device(&sunkbd->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", sunkbd->name, serio->phys);
+}
+
+/*
+ * sunkbd_disconnect() unregisters and closes behind us.
+ */
+
+static void sunkbd_disconnect(struct serio *serio)
+{
+ struct sunkbd *sunkbd = serio->private;
+ input_unregister_device(&sunkbd->dev);
+ serio_close(serio);
+ kfree(sunkbd);
+}
+
+static struct serio_dev sunkbd_dev = {
+ interrupt: sunkbd_interrupt,
+ connect: sunkbd_connect,
+ disconnect: sunkbd_disconnect
+};
+
+/*
+ * The functions for insering/removing us as a module.
+ */
+
+int __init sunkbd_init(void)
+{
+ serio_register_device(&sunkbd_dev);
+ return 0;
+}
+
+void __exit sunkbd_exit(void)
+{
+ serio_unregister_device(&sunkbd_dev);
+}
+
+module_init(sunkbd_init);
+module_exit(sunkbd_exit);
diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c
new file mode 100644
index 000000000000..6f0b2410788a
--- /dev/null
+++ b/drivers/input/keyboard/xtkbd.c
@@ -0,0 +1,157 @@
+/*
+ * $Id: xtkbd.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * XT keyboard driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("XT keyboard driver");
+MODULE_LICENSE("GPL");
+
+#define XTKBD_EMUL0 0xe0
+#define XTKBD_EMUL1 0xe1
+#define XTKBD_KEY 0x7f
+#define XTKBD_RELEASE 0x80
+
+static unsigned char xtkbd_keycode[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 0, 0, 0, 87, 88, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 87, 88, 0, 0, 0, 0,110,111,103,108,105,
+ 106
+};
+
+static char *xtkbd_name = "XT Keyboard";
+
+struct xtkbd {
+ unsigned char keycode[256];
+ struct input_dev dev;
+ struct serio *serio;
+ char phys[32];
+};
+
+void xtkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct xtkbd *xtkbd = serio->private;
+
+ switch (data) {
+ case XTKBD_EMUL0:
+ case XTKBD_EMUL1:
+ return;
+ default:
+
+ if (xtkbd->keycode[data & XTKBD_KEY]) {
+ input_report_key(&xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE));
+ } else {
+ printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n",
+ data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed");
+ }
+ }
+}
+
+void xtkbd_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct xtkbd *xtkbd;
+ int i;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_XT)
+ return;
+
+ if (!(xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL)))
+ return;
+
+ memset(xtkbd, 0, sizeof(struct xtkbd));
+
+ xtkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ xtkbd->serio = serio;
+
+ xtkbd->dev.keycode = xtkbd->keycode;
+ xtkbd->dev.private = xtkbd;
+
+ serio->private = xtkbd;
+
+ if (serio_open(serio, dev)) {
+ kfree(xtkbd);
+ return;
+ }
+
+ memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
+ for (i = 0; i < 255; i++)
+ set_bit(xtkbd->keycode[i], xtkbd->dev.keybit);
+ clear_bit(0, xtkbd->dev.keybit);
+
+ sprintf(xtkbd->phys, "%s/input0", serio->phys);
+
+ xtkbd->dev.name = xtkbd_name;
+ xtkbd->dev.phys = xtkbd->phys;
+ xtkbd->dev.idbus = BUS_XTKBD;
+ xtkbd->dev.idvendor = 0x0001;
+ xtkbd->dev.idproduct = 0x0001;
+ xtkbd->dev.idversion = 0x0100;
+
+ input_register_device(&xtkbd->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", xtkbd_name, serio->phys);
+}
+
+void xtkbd_disconnect(struct serio *serio)
+{
+ struct xtkbd *xtkbd = serio->private;
+ input_unregister_device(&xtkbd->dev);
+ serio_close(serio);
+ kfree(xtkbd);
+}
+
+struct serio_dev xtkbd_dev = {
+ interrupt: xtkbd_interrupt,
+ connect: xtkbd_connect,
+ disconnect: xtkbd_disconnect
+};
+
+int __init xtkbd_init(void)
+{
+ serio_register_device(&xtkbd_dev);
+ return 0;
+}
+
+void __exit xtkbd_exit(void)
+{
+ serio_unregister_device(&xtkbd_dev);
+}
+
+module_init(xtkbd_init);
+module_exit(xtkbd_exit);
diff --git a/drivers/input/mouse/Config.help b/drivers/input/mouse/Config.help
new file mode 100644
index 000000000000..c60ae83ff85d
--- /dev/null
+++ b/drivers/input/mouse/Config.help
@@ -0,0 +1,87 @@
+CONFIG_INPUT_MOUSE
+ Say Y here, and a list of supported mice will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+CONFIG_MOUSE_PS2
+ Say Y here if you have a PS/2 mouse connected to your system. This
+ includes the standard 2 or 3-button PS/2 mouse, as well as PS/2
+ mice with wheels and extra buttons, Microsoft, Logitech or Genius
+ compatible.
+
+ If unsure, say Y.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called psmouse.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_MOUSE_SERIAL
+ Say Y here if you have a serial (RS-232, COM port) mouse connected
+ to your system. This includes Sun, MouseSystems, Microsoft,
+ Logitech and all other compatible serial mice.
+
+ If unsure, say N.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called sermouse.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_MOUSE_INPORT
+ Say Y here if you have an InPort, Microsoft or ATI XL busmouse.
+ They are rather rare these days.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called inport.o. If you want to compile it as a
+ module, say M here and read <file.:Documentation/modules.txt>.
+
+CONFIG_MOUSE_ATIXL
+ Say Y here if your mouse is of the ATI XL variety.
+
+CONFIG_MOUSE_LOGIBM
+ Say Y here if you have a Logitech busmouse.
+ They are rather rare these days.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called logibm.o. If you want to compile it as a
+ module, say M here and read <file.:Documentation/modules.txt>.
+
+CONFIG_MOUSE_PC110PAD
+ Say Y if you have the IBM PC-110 micro-notebook and want its
+ touchscreen supported.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called pc110pad.o. If you want to compile it as a
+ module, say M here and read <file.:Documentation/modules.txt>.
+
+CONFIG_MOUSE_MAPLE
+ Say Y if you have a DreamCast console and a mouse attached to
+ its Maple bus.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called maplemouse.o. If you want to compile it as a
+ module, say M here and read <file.:Documentation/modules.txt>.
+
+CONFIG_MOUSE_AMIGA
+ Say Y here if you have an Amiga and want its native mouse
+ supported by the kernel.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called amimouse.o. If you want to compile it as a
+ module, say M here and read <file.:Documentation/modules.txt>.
+
+CONFIG_MOUSE_ACORN
+ Say Y here if you have the Acorn RiscPC computer and want its
+ native mouse supported.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called rpcmouse.o. If you want to compile it as a
+ module, say M here and read <file.:Documentation/modules.txt>.
diff --git a/drivers/input/mouse/Config.in b/drivers/input/mouse/Config.in
new file mode 100644
index 000000000000..d740c4188ebd
--- /dev/null
+++ b/drivers/input/mouse/Config.in
@@ -0,0 +1,24 @@
+#
+# Mouse driver configuration
+#
+
+bool 'Mice' CONFIG_INPUT_MOUSE
+
+dep_tristate ' PS/2 mouse' CONFIG_MOUSE_PS2 $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_SERIO
+dep_tristate ' Serial mouse' CONFIG_MOUSE_SERIAL $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_SERIO
+
+dep_tristate ' InPort/MS/ATIXL busmouse' CONFIG_MOUSE_INPORT $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_ISA
+if [ "$CONFIG_MOUSE_INPORT" != "n" ]; then
+ bool ' ATI XL variant' CONFIG_MOUSE_ATIXL
+fi
+dep_tristate ' Logitech busmouse' CONFIG_MOUSE_LOGIBM $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_ISA
+dep_tristate ' IBM PC110 touchpad' CONFIG_MOUSE_PC110PAD $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_ISA
+if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then
+ dep_tristate ' Maple bus mouse' CONFIG_MOUSE_MAPLE $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_MAPLE
+fi
+if [ "$CONFIG_AMIGA" = "y" ]; then
+ dep_tristate ' Amiga mouse' CONFIG_MOUSE_AMIGA $CONFIG_INPUT $CONFIG_INPUT_MOUSE
+fi
+if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+ dep_tristate ' Acorn RiscPC mouse' CONFIG_MOUSE_ACORN $CONFIG_INPUT $CONFIG_INPUT_MOUSE
+fi
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
new file mode 100644
index 000000000000..6ac13c13edcf
--- /dev/null
+++ b/drivers/input/mouse/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the mouse drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
+obj-$(CONFIG_MOUSE_ACORN) += rpcmouse.o
+obj-$(CONFIG_MOUSE_INPORT) += inport.o
+obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
+obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
+obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
+obj-$(CONFIG_MOUSE_PS2) += psmouse.o
+obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c
new file mode 100644
index 000000000000..819d96827187
--- /dev/null
+++ b/drivers/input/mouse/amimouse.c
@@ -0,0 +1,147 @@
+/*
+ * $Id: amimouse.c,v 1.9 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Michael Rausch James Banks
+ * Matther Dillon David Giller
+ * Nathan Laredo Linus Torvalds
+ * Johan Myreen Jes Sorensen
+ * Russel King
+ */
+
+/*
+ * Amiga mouse driver for Linux/m68k
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Amiga mouse driver");
+MODULE_LICENSE("GPL");
+
+static int amimouse_used = 0;
+static int amimouse_lastx, amimouse_lasty;
+static struct input_dev amimouse_dev;
+
+static char *amimouse_name = "Amiga mouse";
+static char *amimouse_phys = "amimouse/input0";
+
+static void amimouse_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+ unsigned short joy0dat, potgor;
+ int nx, ny, dx, dy;
+
+ joy0dat = custom.joy0dat;
+
+ nx = joy0dat & 0xff;
+ ny = joy0dat >> 8;
+
+ dx = nx - amimouse_lastx;
+ dy = ny - amimouse_lasty;
+
+ if (dx < -127) dx = (256 + nx) - lastx;
+ if (dx > 127) dx = (nx - 256) - lastx;
+ if (dy < -127) dy = (256 + ny) - lasty;
+ if (dy > 127) dy = (ny - 256) - lasty;
+
+ amimouse_lastx = nx;
+ amimouse_lasty = ny;
+
+ potgor = custom.potgor;
+
+ input_report_rel(&amimouse_dev, REL_X, dx);
+ input_report_rel(&amimouse_dev, REL_Y, dy);
+
+ input_report_key(&amimouse_dev, BTN_LEFT, ciaa.pra & 0x40);
+ input_report_key(&amimouse_dev, BTN_MIDDLE, potgor & 0x0100);
+ input_report_key(&amimouse_dev, BTN_RIGHT, potgor & 0x0400);
+}
+
+static int amimouse_open(struct input_dev *dev)
+{
+ unsigned short joy0dat;
+
+ if (amimouse_used++)
+ return 0;
+
+ joy0dat = custom.joy0dat;
+
+ amimouse_lastx = joy0dat & 0xff;
+ amimouse_lasty = joy0dat >> 8;
+
+ if (request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", NULL)) {
+ amimouse_used--;
+ printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", amimouse_irq);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void amimouse_close(struct input_dev *dev)
+{
+ if (!--amimouse_used)
+ free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt);
+}
+
+static int __init amimouse_init(void)
+{
+ if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE))
+ return -ENODEV;
+
+ amimouse_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ amimouse_dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ amimouse_dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+ amimouse_dev.open = amimouse_open;
+ amimouse_dev.close = amimouse_close;
+
+ amimouse_dev.name = amimouse_name;
+ amimouse_dev.phys = amimouse_phys;
+ amimouse_dev.idbus = BUS_AMIGA;
+ amimouse_dev.idvendor = 0x0001;
+ amimouse_dev.idproduct = 0x0002;
+ amimouse_dev.idversion = 0x0100;
+
+ input_register_device(&amimouse_dev);
+
+ printk(KERN_INFO "input: %s at joy0dat\n", amimouse_name);
+}
+
+static void __exit amimouse_exit(void)
+{
+ input_unregister_device(&amimouse_dev);
+}
+
+module_init(amimouse_init);
+module_exit(amimouse_exit);
diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c
new file mode 100644
index 000000000000..9931ff92835e
--- /dev/null
+++ b/drivers/input/mouse/inport.c
@@ -0,0 +1,193 @@
+/*
+ * $Id: inport.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Teemu Rantanen Derrick Cole
+ * Peter Cervasio Christoph Niemann
+ * Philip Blundell Russell King
+ * Bob Harris
+ */
+
+/*
+ * Inport (ATI XL and Microsoft) busmouse driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define INPORT_BASE 0x23c
+#define INPORT_EXTENT 4
+
+#define INPORT_CONTROL_PORT INPORT_BASE + 0
+#define INPORT_DATA_PORT INPORT_BASE + 1
+#define INPORT_SIGNATURE_PORT INPORT_BASE + 2
+
+#define INPORT_REG_BTNS 0x00
+#define INPORT_REG_X 0x01
+#define INPORT_REG_Y 0x02
+#define INPORT_REG_MODE 0x07
+#define INPORT_RESET 0x80
+
+#ifdef CONFIG_INPUT_ATIXL
+#define INPORT_NAME "ATI XL Mouse"
+#define INPORT_VENDOR 0x0002
+#define INPORT_SPEED_30HZ 0x01
+#define INPORT_SPEED_50HZ 0x02
+#define INPORT_SPEED_100HZ 0x03
+#define INPORT_SPEED_200HZ 0x04
+#define INPORT_MODE_BASE INPORT_SPEED_100HZ
+#define INPORT_MODE_IRQ 0x08
+#else
+#define INPORT_NAME "Microsoft InPort Mouse"
+#define INPORT_VENDOR 0x0001
+#define INPORT_MODE_BASE 0x10
+#define INPORT_MODE_IRQ 0x01
+#endif
+#define INPORT_MODE_HOLD 0x20
+
+#define INPORT_IRQ 5
+
+MODULE_PARM(inport_irq, "i");
+
+static int inport_irq = INPORT_IRQ;
+static int inport_used = 0;
+
+static void inport_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int inport_open(struct input_dev *dev)
+{
+ if (!inport_used++) {
+ if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL))
+ return -EBUSY;
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+ }
+
+ return 0;
+}
+
+static void inport_close(struct input_dev *dev)
+{
+ if (!--inport_used) {
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
+ free_irq(inport_irq, NULL);
+ }
+}
+
+static struct input_dev inport_dev = {
+ evbit: { BIT(EV_KEY) | BIT(EV_REL) },
+ keybit: { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
+ relbit: { BIT(REL_X) | BIT(REL_Y) },
+ open: inport_open,
+ close: inport_close,
+ name: INPORT_NAME,
+ phys: "isa023c/input0",
+ idbus: BUS_ISA,
+ idvendor: INPORT_VENDOR,
+ idproduct: 0x0001,
+ idversion: 0x0100,
+};
+
+static void inport_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned char buttons;
+
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+ outb(INPORT_REG_X, INPORT_CONTROL_PORT);
+ input_report_rel(&inport_dev, REL_X, inb(INPORT_DATA_PORT));
+
+ outb(INPORT_REG_Y, INPORT_CONTROL_PORT);
+ input_report_rel(&inport_dev, REL_Y, inb(INPORT_DATA_PORT));
+
+ outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT);
+ buttons = inb(INPORT_DATA_PORT);
+
+ input_report_key(&inport_dev, BTN_MIDDLE, buttons & 1);
+ input_report_key(&inport_dev, BTN_LEFT, buttons & 2);
+ input_report_key(&inport_dev, BTN_RIGHT, buttons & 4);
+
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+}
+
+#ifndef MODULE
+static int __init inport_setup(char *str)
+{
+ int ints[4];
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+ if (ints[0] > 0) inport_irq = ints[1];
+ return 1;
+}
+__setup("inport_irq=", inport_setup);
+#endif
+
+static int __init inport_init(void)
+{
+ unsigned char a,b,c;
+
+ if (check_region(INPORT_BASE, INPORT_EXTENT))
+ return -EBUSY;
+
+ a = inb(INPORT_SIGNATURE_PORT);
+ b = inb(INPORT_SIGNATURE_PORT);
+ c = inb(INPORT_SIGNATURE_PORT);
+ if (( a == b ) || ( a != c ))
+ return -ENODEV;
+
+ outb(INPORT_RESET, INPORT_CONTROL_PORT);
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+ request_region(INPORT_BASE, INPORT_EXTENT, "inport");
+
+ input_register_device(&inport_dev);
+
+ printk(KERN_INFO "input: " INPORT_NAME " at %#x irq %d\n",
+ INPORT_BASE, inport_irq);
+
+ return 0;
+}
+
+static void __exit inport_exit(void)
+{
+ input_unregister_device(&inport_dev);
+ release_region(INPORT_BASE, INPORT_EXTENT);
+}
+
+module_init(inport_init);
+module_exit(inport_exit);
diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c
new file mode 100644
index 000000000000..f1b34343de90
--- /dev/null
+++ b/drivers/input/mouse/logibm.c
@@ -0,0 +1,182 @@
+/*
+ * $Id: logibm.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * James Banks Matthew Dillon
+ * David Giller Nathan Laredo
+ * Linus Torvalds Johan Myreen
+ * Cliff Matthews Philip Blundell
+ * Russell King
+ */
+
+/*
+ * Logitech Bus Mouse Driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Logitech busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define LOGIBM_BASE 0x23c
+#define LOGIBM_EXTENT 4
+
+#define LOGIBM_DATA_PORT LOGIBM_BASE + 0
+#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1
+#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2
+#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3
+
+#define LOGIBM_ENABLE_IRQ 0x00
+#define LOGIBM_DISABLE_IRQ 0x10
+#define LOGIBM_READ_X_LOW 0x80
+#define LOGIBM_READ_X_HIGH 0xa0
+#define LOGIBM_READ_Y_LOW 0xc0
+#define LOGIBM_READ_Y_HIGH 0xe0
+
+#define LOGIBM_DEFAULT_MODE 0x90
+#define LOGIBM_CONFIG_BYTE 0x91
+#define LOGIBM_SIGNATURE_BYTE 0xa5
+
+#define LOGIBM_IRQ 5
+
+MODULE_PARM(logibm_irq, "i");
+
+static int logibm_irq = LOGIBM_IRQ;
+static int logibm_used = 0;
+
+static void logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int logibm_open(struct input_dev *dev)
+{
+ if (logibm_used++)
+ return 0;
+ if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) {
+ logibm_used--;
+ printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq);
+ return -EBUSY;
+ }
+ outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
+ return 0;
+}
+
+static void logibm_close(struct input_dev *dev)
+{
+ if (--logibm_used)
+ return;
+ outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
+ free_irq(logibm_irq, NULL);
+}
+
+static struct input_dev logibm_dev = {
+ evbit: { BIT(EV_KEY) | BIT(EV_REL) },
+ keybit: { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
+ relbit: { BIT(REL_X) | BIT(REL_Y) },
+ open: logibm_open,
+ close: logibm_close,
+ name: "Logitech bus mouse",
+ phys: "isa023c/input0",
+ idbus: BUS_ISA,
+ idvendor: 0x0003,
+ idproduct: 0x0001,
+ idversion: 0x0100,
+};
+
+static void logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ char dx, dy;
+ unsigned char buttons;
+
+ outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT);
+ dx = (inb(LOGIBM_DATA_PORT) & 0xf);
+ outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT);
+ dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4;
+ outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT);
+ dy = (inb(LOGIBM_DATA_PORT) & 0xf);
+ outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT);
+ buttons = inb(LOGIBM_DATA_PORT);
+ dy |= (buttons & 0xf) << 4;
+ buttons = ~buttons;
+
+ input_report_rel(&logibm_dev, REL_X, dx);
+ input_report_rel(&logibm_dev, REL_Y, 255 - dy);
+ input_report_key(&logibm_dev, BTN_MIDDLE, buttons & 1);
+ input_report_key(&logibm_dev, BTN_LEFT, buttons & 2);
+ input_report_key(&logibm_dev, BTN_RIGHT, buttons & 4);
+}
+
+#ifndef MODULE
+static int __init logibm_setup(char *str)
+{
+ int ints[4];
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+ if (ints[0] > 0) logibm_irq = ints[1];
+ return 1;
+}
+__setup("logibm_irq=", logibm_setup);
+#endif
+
+static int __init logibm_init(void)
+{
+ if (request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) {
+ printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE);
+ return -EBUSY;
+ }
+
+ outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT);
+ outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT);
+ udelay(100);
+
+ if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) {
+ release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+ printk(KERN_ERR "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE);
+ return -ENODEV;
+ }
+
+ outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT);
+ outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
+
+ input_register_device(&logibm_dev);
+
+ printk(KERN_INFO "input: Logitech bus mouse at %#x irq %d\n", LOGIBM_BASE, logibm_irq);
+
+ return 0;
+}
+
+static void __exit logibm_exit(void)
+{
+ input_unregister_device(&logibm_dev);
+ release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+}
+
+module_init(logibm_init);
+module_exit(logibm_exit);
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
new file mode 100644
index 000000000000..34d90206a5b8
--- /dev/null
+++ b/drivers/input/mouse/maplemouse.c
@@ -0,0 +1,137 @@
+/*
+ * $Id: maplemouse.c,v 1.1 2001/11/02 17:27:32 jsimmons Exp $
+ * SEGA Dreamcast mouse driver
+ * Based on drivers/usb/usbmouse.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("YAEGASHI Takeshi <t@keshi.org>");
+MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
+
+struct dc_mouse {
+ struct input_dev dev;
+ int open;
+};
+
+
+static void dc_mouse_callback(struct mapleq *mq)
+{
+ int buttons, relx, rely, relz;
+ struct maple_device *mapledev = mq->dev;
+ struct dc_mouse *mouse = mapledev->private_data;
+ struct input_dev *dev = &mouse->dev;
+ unsigned char *res = mq->recvbuf;
+
+ buttons = ~res[8];
+ relx=*(unsigned short *)(res+12)-512;
+ rely=*(unsigned short *)(res+14)-512;
+ relz=*(unsigned short *)(res+16)-512;
+
+ input_report_key(dev, BTN_LEFT, buttons&4);
+ input_report_key(dev, BTN_MIDDLE, buttons&9);
+ input_report_key(dev, BTN_RIGHT, buttons&2);
+ input_report_rel(dev, REL_X, relx);
+ input_report_rel(dev, REL_Y, rely);
+ input_report_rel(dev, REL_WHEEL, relz);
+}
+
+
+static int dc_mouse_open(struct input_dev *dev)
+{
+ struct dc_mouse *mouse = dev->private;
+ mouse->open++;
+ return 0;
+}
+
+
+static void dc_mouse_close(struct input_dev *dev)
+{
+ struct dc_mouse *mouse = dev->private;
+ mouse->open--;
+}
+
+
+static int dc_mouse_connect(struct maple_device *dev)
+{
+ unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]);
+ struct dc_mouse *mouse;
+
+ if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL)))
+ return -1;
+ memset(mouse, 0, sizeof(struct dc_mouse));
+
+ dev->private_data = mouse;
+
+ mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL);
+
+ mouse->dev.private = mouse;
+ mouse->dev.open = dc_mouse_open;
+ mouse->dev.close = dc_mouse_close;
+ mouse->dev.event = NULL;
+
+ mouse->dev.name = dev->product_name;
+ mouse->dev.idbus = BUS_MAPLE;
+
+ input_register_device(&mouse->dev);
+
+ maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE);
+
+ printk(KERN_INFO "input%d: mouse(0x%lx): %s\n",
+ mouse->dev.number, data, mouse->dev.name);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+
+static void dc_mouse_disconnect(struct maple_device *dev)
+{
+ struct dc_mouse *mouse = dev->private_data;
+
+ input_unregister_device(&mouse->dev);
+
+ kfree(mouse);
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct maple_driver dc_mouse_driver = {
+ function: MAPLE_FUNC_MOUSE,
+ name: "Dreamcast mouse",
+ connect: dc_mouse_connect,
+ disconnect: dc_mouse_disconnect,
+};
+
+
+static int __init dc_mouse_init(void)
+{
+ maple_register_driver(&dc_mouse_driver);
+ return 0;
+}
+
+
+static void __exit dc_mouse_exit(void)
+{
+ maple_unregister_driver(&dc_mouse_driver);
+}
+
+
+module_init(dc_mouse_init);
+module_exit(dc_mouse_exit);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c
new file mode 100644
index 000000000000..61ee96685dcf
--- /dev/null
+++ b/drivers/input/mouse/pc110pad.c
@@ -0,0 +1,163 @@
+/*
+ * $Id: pc110pad.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Alan Cox Robin O'Leary
+ */
+
+/*
+ * IBM PC110 touchpad driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/input.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("IBM PC110 touchpad driver");
+MODULE_LICENSE("GPL");
+
+#define PC110PAD_OFF 0x30
+#define PC110PAD_ON 0x38
+
+static int pc110pad_irq = 10;
+static int pc110pad_io = 0x15e0;
+
+static struct input_dev pc110pad_dev;
+static int pc110pad_data[3];
+static int pc110pad_count;
+static int pc110pad_used;
+
+static char *pc110pad_name = "IBM PC110 TouchPad";
+static char *pc110pad_phys = "isa15e0/input0";
+
+static void pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs)
+{
+ int value = inb_p(pc110pad_io);
+ int handshake = inb_p(pc110pad_io + 2);
+
+ outb_p(handshake | 1, pc110pad_io + 2);
+ outb_p(handshake & ~1, pc110pad_io + 2);
+ inb_p(0x64);
+
+ pc110pad_data[pc110pad_count++] = value;
+
+ if (pc110pad_count < 3) return;
+
+ input_report_key(&pc110pad_dev, BTN_TOUCH,
+ pc110pad_data[0] & 0x01);
+ input_report_abs(&pc110pad_dev, ABS_X,
+ pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100));
+ input_report_abs(&pc110pad_dev, ABS_Y,
+ pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80));
+
+ pc110pad_count = 0;
+}
+
+static void pc110pad_close(struct input_dev *dev)
+{
+ if (!--pc110pad_used)
+ outb(PC110PAD_OFF, pc110pad_io + 2);
+}
+
+static int pc110pad_open(struct input_dev *dev)
+{
+ unsigned long flags;
+
+ if (pc110pad_used++)
+ return 0;
+
+ save_flags(flags);
+ cli();
+ pc110pad_interrupt(0,0,0);
+ pc110pad_interrupt(0,0,0);
+ pc110pad_interrupt(0,0,0);
+ outb(PC110PAD_ON, pc110pad_io + 2);
+ pc110pad_count = 0;
+ restore_flags(flags);
+
+ return 0;
+}
+
+static int __init pc110pad_init(void)
+{
+ if (request_region(pc110pad_io, 4, "pc110pad"))
+ {
+ printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", pc110pad_io, pc110pad_io + 4);
+ return -EBUSY;
+ }
+
+ outb(PC110PAD_OFF, pc110pad_io + 2);
+
+ if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", 0))
+ {
+ release_region(pc110pad_io, 4);
+ printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
+ return -EBUSY;
+ }
+
+ pc110pad_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ pc110pad_dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+ pc110pad_dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+ pc110pad_dev.absmax[ABS_X] = 0x1ff;
+ pc110pad_dev.absmax[ABS_Y] = 0x0ff;
+
+ pc110pad_dev.open = pc110pad_open;
+ pc110pad_dev.close = pc110pad_close;
+
+ pc110pad_dev.name = pc110pad_name;
+ pc110pad_dev.phys = pc110pad_phys;
+ pc110pad_dev.idbus = BUS_ISA;
+ pc110pad_dev.idvendor = 0x0003;
+ pc110pad_dev.idproduct = 0x0001;
+ pc110pad_dev.idversion = 0x0100;
+
+ input_register_device(&pc110pad_dev);
+
+ printk(KERN_INFO "input: %s at %#x irq %d\n",
+ pc110pad_name, pc110pad_io, pc110pad_irq);
+
+ return 0;
+}
+
+static void __exit pc110pad_exit(void)
+{
+ input_unregister_device(&pc110pad_dev);
+
+ outb(PC110PAD_OFF, pc110pad_io + 2);
+
+ free_irq(pc110pad_irq, 0);
+ release_region(pc110pad_io, 4);
+}
+
+module_init(pc110pad_init);
+module_exit(pc110pad_exit);
diff --git a/drivers/input/mouse/psmouse.c b/drivers/input/mouse/psmouse.c
new file mode 100644
index 000000000000..ba846a57caa4
--- /dev/null
+++ b/drivers/input/mouse/psmouse.c
@@ -0,0 +1,652 @@
+/*
+ * $Id: psmouse.c,v 1.18 2002/03/13 10:03:43 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/tqueue.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("PS/2 mouse driver");
+MODULE_LICENSE("GPL");
+
+#define PSMOUSE_CMD_SETSCALE11 0x00e6
+#define PSMOUSE_CMD_SETRES 0x10e8
+#define PSMOUSE_CMD_GETINFO 0x03e9
+#define PSMOUSE_CMD_SETSTREAM 0x00ea
+#define PSMOUSE_CMD_POLL 0x03eb
+#define PSMOUSE_CMD_GETID 0x01f2
+#define PSMOUSE_CMD_SETRATE 0x10f3
+#define PSMOUSE_CMD_ENABLE 0x00f4
+#define PSMOUSE_CMD_RESET_DIS 0x00f6
+
+#define PSMOUSE_RET_BAT 0xaa
+#define PSMOUSE_RET_ACK 0xfa
+#define PSMOUSE_RET_NAK 0xfe
+
+struct psmouse {
+ struct input_dev dev;
+ struct serio *serio;
+ char *vendor;
+ char *name;
+ struct tq_struct tq;
+ unsigned char cmdbuf[8];
+ unsigned char packet[8];
+ unsigned char cmdcnt;
+ unsigned char pktcnt;
+ unsigned char type;
+ unsigned long last;
+ char acking;
+ char ack;
+ char error;
+ char devname[64];
+ char phys[32];
+};
+
+#define PSMOUSE_PS2 1
+#define PSMOUSE_PS2PP 2
+#define PSMOUSE_PS2TPP 3
+#define PSMOUSE_GENPS 4
+#define PSMOUSE_IMPS 5
+#define PSMOUSE_IMEX 6
+
+static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2" };
+
+/*
+ * psmouse_process_packet() anlyzes the PS/2 mouse packet contents and
+ * reports relevant events to the input module.
+ */
+
+static void psmouse_process_packet(struct psmouse *psmouse)
+{
+ struct input_dev *dev = &psmouse->dev;
+ unsigned char *packet = psmouse->packet;
+
+/*
+ * The PS2++ protocol is a little bit complex
+ */
+
+ if (psmouse->type == PSMOUSE_PS2PP || psmouse->type == PSMOUSE_PS2TPP) {
+
+ if ((packet[0] & 0x40) == 0x40 && (int) packet[1] - (int) ((packet[0] & 0x10) << 4) > 191 ) {
+
+ switch (((packet[1] >> 4) & 0x03) | ((packet[0] >> 2) & 0xc0)) {
+
+ case 1: /* Mouse extra info */
+
+ input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
+ (int) (packet[2] & 7) - (int) (packet[2] & 8));
+ input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
+ input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
+
+ break;
+
+ case 3: /* TouchPad extra info */
+
+ input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
+ (int) ((packet[2] >> 4) & 7) - (int) ((packet[2] >> 4) & 8));
+ packet[0] = packet[2] | 0x08;
+
+ break;
+
+ default:
+
+ printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
+ ((packet[1] >> 4) & 0x03) | ((packet[0] >> 2) & 0xc0));
+
+ }
+
+ packet[0] &= 0x0f;
+ packet[1] = 0;
+ packet[2] = 0;
+
+ }
+ }
+
+/*
+ * Scroll wheel on IntelliMice, scroll buttons on NetMice
+ */
+
+ if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
+ input_report_rel(dev, REL_WHEEL, (signed char) packet[3]);
+
+/*
+ * Scroll wheel and buttons on IntelliMouse Explorer
+ */
+
+ if (psmouse->type == PSMOUSE_IMEX) {
+ input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 7) - (int) (packet[2] & 8));
+ input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
+ input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
+ }
+
+/*
+ * Extra buttons on Genius NewNet 3D
+ */
+
+ if (psmouse->type == PSMOUSE_GENPS) {
+ input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
+ input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
+ }
+
+/*
+ * Generic PS/2 Mouse
+ */
+
+ input_report_key(dev, BTN_LEFT, packet[0] & 1);
+ input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+ input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
+
+ input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
+ input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+
+}
+
+/*
+ * psmouse_interrupt() handles incoming characters, either gathering them into
+ * packets or passing them to the command routine as command output.
+ */
+
+static void psmouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct psmouse *psmouse = serio->private;
+
+ if (psmouse->acking) {
+ switch (data) {
+ case PSMOUSE_RET_ACK:
+ psmouse->ack = 1;
+ break;
+ case PSMOUSE_RET_NAK:
+ psmouse->ack = -1;
+ break;
+ default:
+ psmouse->ack = 1; /* Workaround for mice which don't ACK the Get ID command */
+ if (psmouse->cmdcnt)
+ psmouse->cmdbuf[--psmouse->cmdcnt] = data;
+ break;
+ }
+ psmouse->acking = 0;
+ return;
+ }
+
+ if (psmouse->cmdcnt) {
+ psmouse->cmdbuf[--psmouse->cmdcnt] = data;
+ return;
+ }
+
+ if (psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/20)) {
+ printk(KERN_WARNING "psmouse.c: Lost synchronization, throwing %d bytes away.\n", psmouse->pktcnt);
+ psmouse->pktcnt = 0;
+ }
+
+ psmouse->last = jiffies;
+ psmouse->packet[psmouse->pktcnt++] = data;
+
+ if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) {
+ if ((psmouse->packet[0] & 0x08) == 0x08) psmouse_process_packet(psmouse);
+ psmouse->pktcnt = 0;
+ return;
+ }
+
+ if (psmouse->pktcnt == 1 && psmouse->packet[0] == PSMOUSE_RET_BAT) {
+ queue_task(&psmouse->tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ return;
+ }
+}
+
+/*
+ * psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge.
+ * It doesn't handle retransmission, though it could - because when there would
+ * be need for retransmissions, the mouse has to be replaced anyway.
+ */
+
+static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte)
+{
+ int timeout = 10000; /* 100 msec */
+ psmouse->ack = 0;
+ psmouse->acking = 1;
+
+ serio_write(psmouse->serio, byte);
+ while (!psmouse->ack && timeout--) udelay(10);
+
+ return -(psmouse->ack <= 0);
+}
+
+/*
+ * psmouse_command() sends a command and its parameters to the mouse,
+ * then waits for the response and puts it in the param array.
+ */
+
+static int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command)
+{
+ int timeout = 500000; /* 500 msec */
+ int send = (command >> 12) & 0xf;
+ int receive = (command >> 8) & 0xf;
+ int i;
+
+ psmouse->cmdcnt = receive;
+
+ if (command & 0xff)
+ if (psmouse_sendbyte(psmouse, command & 0xff))
+ return (psmouse->cmdcnt = 0) - 1;
+
+ for (i = 0; i < send; i++)
+ if (psmouse_sendbyte(psmouse, param[i]))
+ return (psmouse->cmdcnt = 0) - 1;
+
+ while (psmouse->cmdcnt && timeout--) udelay(1);
+
+ for (i = 0; i < receive; i++)
+ param[i] = psmouse->cmdbuf[(receive - 1) - i];
+
+ if (psmouse->cmdcnt)
+ return (psmouse->cmdcnt = 0) - 1;
+
+ return 0;
+}
+
+/*
+ * psmouse_ps2pp_cmd() sends a PS2++ command, sliced into two bit
+ * pieces through the SETRES command. This is needed to send extended
+ * commands to mice on notebooks that try to understand the PS/2 protocol
+ * Ugly.
+ */
+
+static int psmouse_ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
+{
+ unsigned char d;
+ int i;
+
+ if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11))
+ return -1;
+
+ for (i = 6; i >= 0; i -= 2) {
+ d = (command >> i) & 3;
+ if(psmouse_command(psmouse, &d, PSMOUSE_CMD_SETRES))
+ return -1;
+ }
+
+ if (psmouse_command(psmouse, param, PSMOUSE_CMD_POLL))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
+ * the mouse may have.
+ */
+
+static int psmouse_extensions(struct psmouse *psmouse)
+{
+ unsigned char param[4];
+
+ param[0] = 0;
+ psmouse->vendor = "Generic";
+ psmouse->name = "Mouse";
+
+/*
+ * Try Genius NetMouse magic init.
+ */
+
+ param[0] = 3;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
+ psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
+ psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
+ psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
+ psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
+
+ if (param[0] == 0x00 && param[1] == 0x33 && param[2] == 0x55) {
+ psmouse->vendor = "Genius";
+ psmouse->name = "Mouse";
+
+ set_bit(BTN_EXTRA, psmouse->dev.keybit);
+ set_bit(BTN_SIDE, psmouse->dev.keybit);
+ set_bit(REL_WHEEL, psmouse->dev.relbit);
+
+ return PSMOUSE_GENPS;
+ }
+
+/*
+ * Try Logitech magic ID.
+ */
+
+ param[0] = 0;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
+ psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
+ psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
+ psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
+ psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
+
+ if (param[1]) {
+
+ int i;
+ static int logitech_4btn[] = { 12, 40, 41, 42, 43, 73, 80, -1 };
+ static int logitech_wheel[] = { 75, 76, 80, 81, 83, 88, -1 };
+ static int logitech_ps2pp[] = { 12, 13, 40, 41, 42, 43, 50, 51, 52, 53, 73, 75,
+ 76, 80, 81, 83, 88, 96, 97, -1 };
+
+ int devicetype = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
+
+ psmouse->vendor = "Logitech";
+ psmouse->name = "Mouse";
+
+ if (param[1] < 3)
+ clear_bit(BTN_MIDDLE, psmouse->dev.keybit);
+ if (param[1] < 2)
+ clear_bit(BTN_RIGHT, psmouse->dev.keybit);
+
+ psmouse->type = PSMOUSE_PS2;
+
+ for (i = 0; logitech_ps2pp[i] != -1; i++)
+ if (logitech_ps2pp[i] == devicetype) psmouse->type = PSMOUSE_PS2PP;
+
+ if (psmouse->type != PSMOUSE_PS2PP) return PSMOUSE_PS2;
+
+ for (i = 0; logitech_4btn[i] != -1; i++)
+ if (logitech_4btn[i] == devicetype) set_bit(BTN_SIDE, psmouse->dev.keybit);
+
+ for (i = 0; logitech_wheel[i] != -1; i++)
+ if (logitech_wheel[i] == devicetype) set_bit(REL_WHEEL, psmouse->dev.relbit);
+
+/*
+ * Do Logitech PS2++ / PS2T++ magic init.
+ */
+
+ if (devicetype == 97) { /* TouchPad 3 */
+
+ set_bit(REL_WHEEL, psmouse->dev.relbit);
+ set_bit(REL_HWHEEL, psmouse->dev.relbit);
+
+ param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; /* Unprotect RAM */
+ psmouse_command(psmouse, param, 0x30d1);
+ param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b; /* Enable features */
+ psmouse_command(psmouse, param, 0x30d1);
+ param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3; /* Enable PS2++ */
+ psmouse_command(psmouse, param, 0x30d1);
+
+ param[0] = 0;
+ if (!psmouse_command(psmouse, param, 0x13d1) &&
+ param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14)
+ return PSMOUSE_PS2TPP;
+
+ } else {
+ psmouse_ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
+ psmouse_ps2pp_cmd(psmouse, param, 0xDB);
+
+ if ((param[0] & 0x78) == 0x48 && (param[1] & 0xf3) == 0xc2 &&
+ (param[2] & 3) == ((param[1] >> 2) & 3))
+ return PSMOUSE_PS2PP;
+ }
+
+ }
+
+/*
+ * Try IntelliMouse magic init.
+ */
+
+ param[0] = 200;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 100;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 80;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
+ psmouse_command(psmouse, param, PSMOUSE_CMD_GETID);
+
+ if (param[0] == 3) {
+
+ set_bit(REL_WHEEL, psmouse->dev.relbit);
+
+/*
+ * Try IntelliMouse Explorer magic init.
+ */
+
+ param[0] = 200;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 200;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 80;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
+ psmouse_command(psmouse, param, PSMOUSE_CMD_GETID);
+
+ if (param[0] == 4) {
+
+ psmouse->vendor = "Microsoft";
+ psmouse->name = "IntelliMouse Explorer";
+
+ set_bit(BTN_SIDE, psmouse->dev.keybit);
+ set_bit(BTN_EXTRA, psmouse->dev.keybit);
+
+ return PSMOUSE_IMEX;
+ }
+
+ psmouse->vendor = "Microsoft";
+ psmouse->name = "IntelliMouse";
+
+ return PSMOUSE_IMPS;
+ }
+
+/*
+ * Okay, all failed, we have a standard mouse here. The number of the buttons is
+ * still a question, though.
+ */
+
+ psmouse->vendor = "Generic";
+ psmouse->name = "Mouse";
+
+ return PSMOUSE_PS2;
+}
+
+/*
+ * psmouse_probe() probes for a PS/2 mouse.
+ */
+
+static int psmouse_probe(struct psmouse *psmouse)
+{
+ unsigned char param[2];
+
+/*
+ * First we reset and disable the mouse.
+ */
+
+ if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_RESET_DIS))
+ return -1;
+
+/*
+ * Next, we check if it's a mouse. It should send 0x00 or 0x03
+ * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
+ */
+
+ param[0] = param[1] = 0xa5;
+
+ if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETID))
+ return -1;
+
+ if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04)
+ return -1;
+
+/*
+ * And here we try to determine if it has any extensions over the
+ * basic PS/2 3-button mouse.
+ */
+
+ return psmouse->type = psmouse_extensions(psmouse);
+}
+
+/*
+ * psmouse_initialize() initializes the mouse to a sane state.
+ */
+
+static void psmouse_initialize(struct psmouse *psmouse)
+{
+ unsigned char param[2];
+
+/*
+ * We set the mouse report rate to a highest possible value.
+ * We try 100 first in case mouse fails to set 200.
+ */
+
+ param[0] = 100;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
+
+ param[0] = 200;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
+
+/*
+ * We also set the resolution and scaling.
+ */
+
+ param[0] = 3;
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
+ psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
+
+/*
+ * We set the mouse into streaming mode.
+ */
+
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM);
+
+/*
+ * Last, we enable the mouse so that we get reports from it.
+ */
+
+ if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) {
+ printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys);
+ }
+
+}
+
+/*
+ * psmouse_disconnect() cleans up after we don't want talk
+ * to the mouse anymore.
+ */
+
+static void psmouse_disconnect(struct serio *serio)
+{
+ struct psmouse *psmouse = serio->private;
+ input_unregister_device(&psmouse->dev);
+ serio_close(serio);
+ kfree(psmouse);
+}
+
+/*
+ * psmouse_powerup() is called when we get the powerup
+ * sequence - 0xaa [0x00], so that the mouse/kbd is re-probed.
+ */
+
+static void psmouse_powerup(void *data)
+{
+ struct psmouse *psmouse = data;
+
+ if (psmouse->packet[0] == PSMOUSE_RET_BAT && (psmouse->pktcnt == 1 ||
+ (psmouse->pktcnt == 2 && psmouse->packet[1] == 0x00))) {
+ mdelay(40); /* FIXME!!! Wait some nicer way */
+ serio_rescan(psmouse->serio);
+ }
+}
+
+/*
+ * psmouse_connect() is a callback form the serio module when
+ * an unhandled serio port is found.
+ */
+
+static void psmouse_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct psmouse *psmouse;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_8042)
+ return;
+
+ if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL)))
+ return;
+
+ memset(psmouse, 0, sizeof(struct psmouse));
+
+ psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+ psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+
+ psmouse->serio = serio;
+ psmouse->dev.private = psmouse;
+ psmouse->tq.routine = psmouse_powerup;
+ psmouse->tq.data = psmouse;
+
+ serio->private = psmouse;
+
+ if (serio_open(serio, dev)) {
+ kfree(psmouse);
+ return;
+ }
+
+ if (psmouse_probe(psmouse) <= 0) {
+ serio_close(serio);
+ kfree(psmouse);
+ return;
+ }
+
+ sprintf(psmouse->devname, "%s %s %s",
+ psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name);
+ sprintf(psmouse->phys, "%s/input0",
+ serio->phys);
+
+ psmouse->dev.name = psmouse->devname;
+ psmouse->dev.phys = psmouse->phys;
+ psmouse->dev.idbus = BUS_I8042;
+ psmouse->dev.idvendor = psmouse->type;
+ psmouse->dev.idproduct = 0x0002;
+ psmouse->dev.idversion = 0x0100;
+
+ input_register_device(&psmouse->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys);
+
+ psmouse_initialize(psmouse);
+}
+
+static struct serio_dev psmouse_dev = {
+ interrupt: psmouse_interrupt,
+ connect: psmouse_connect,
+ disconnect: psmouse_disconnect
+};
+
+int __init psmouse_init(void)
+{
+ serio_register_device(&psmouse_dev);
+ return 0;
+}
+
+void __exit psmouse_exit(void)
+{
+ serio_unregister_device(&psmouse_dev);
+}
+
+module_init(psmouse_init);
+module_exit(psmouse_exit);
diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c
new file mode 100644
index 000000000000..04d16de5defc
--- /dev/null
+++ b/drivers/input/mouse/rpcmouse.c
@@ -0,0 +1,111 @@
+/*
+ * $Id: rpcmouse.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Russel King
+ */
+
+/*
+ * Acorn RiscPC mouse driver for Linux/ARM
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/iomd.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Acorn RiscPC mouse driver");
+MODULE_LICENSE("GPL");
+
+#define IOMD_MOUSEBTN 0x800C4000
+
+static short rpcmouse_lastx, rpcmouse_lasty;
+
+static struct input_dev rpcmouse_dev = {
+ evbit: { BIT(EV_KEY) | BIT(EV_REL) },
+ keybit: { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
+ relbit: { BIT(REL_X) | BIT(REL_Y) },
+ name: "Acorn RiscPC Mouse",
+ phys: "rpcmouse/input0",
+ idbus: BUS_ISA,
+ idvendor: 0x0005,
+ idproduct: 0x0001,
+ idversion: 0x0100,
+};
+
+static void rpcmouse_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ short x, y, dx, dy, b;
+
+ x = (short) inl(IOMD_MOUSEX);
+ y = (short) inl(IOMD_MOUSEY);
+ b = (short) inl(IOMD_MOUSEBTN);
+
+ dx = x - rpcmouse_lastx;
+ dy = y - rpcmouse_lasty;
+
+ rpcmouse_lastx = x;
+ rpcmouse_lasty = y;
+
+ input_report_rel(&rpcmouse_dev, REL_X, dx);
+ input_report_rel(&rpcmouse_dev, REL_Y, dy);
+
+ input_report_key(&amimouse_dev, BTN_LEFT, buttons & 0x10);
+ input_report_key(&amimouse_dev, BTN_MIDDLE, buttons & 0x20);
+ input_report_key(&amimouse_dev, BTN_RIGHT, buttons & 0x40);
+}
+
+static int __init rpcmouse_init(void)
+{
+ rpcmouse_lastx = (short) inl(IOMD_MOUSEX);
+ rpcmouse_lasty = (short) inl(IOMD_MOUSEY);
+
+ if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, SA_SHIRQ, "rpcmouse", NULL)) {
+ printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n");
+ return -1;
+ }
+
+ input_register_device(&rpcmouse_dev);
+ printk(KERN_INFO "input%d: Acorn RiscPC mouse irq %d", IRQ_VSYNCPULSE);
+
+ return 0;
+}
+
+static void __exit rpcmouse_exit(void)
+{
+ input_unregister_device(&rpcmouse_dev);
+ free_irq(IRQ_VSYNCPULSE, NULL);
+}
+
+module_init(rpcmouse_init);
+module_exit(rpcmouse_exit);
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
new file mode 100644
index 000000000000..4dd1a4e9033c
--- /dev/null
+++ b/drivers/input/mouse/sermouse.c
@@ -0,0 +1,299 @@
+/*
+ * $Id: sermouse.c,v 1.17 2002/03/13 10:03:43 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Serial mouse driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Serial mouse driver");
+MODULE_LICENSE("GPL");
+
+static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
+ "Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse",
+ "Logitech MZ++ Mouse"};
+
+struct sermouse {
+ struct input_dev dev;
+ signed char buf[8];
+ unsigned char count;
+ unsigned char type;
+ unsigned long last;
+ char phys[32];
+};
+
+/*
+ * sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and
+ * applies some prediction to the data, resulting in 96 updates per
+ * second, which is as good as a PS/2 or USB mouse.
+ */
+
+static void sermouse_process_msc(struct sermouse *sermouse, signed char data)
+{
+ struct input_dev *dev = &sermouse->dev;
+ signed char *buf = sermouse->buf;
+
+ switch (sermouse->count) {
+
+ case 0:
+ if ((data & 0xf8) != 0x80) return;
+ input_report_key(dev, BTN_LEFT, !(data & 4));
+ input_report_key(dev, BTN_RIGHT, !(data & 1));
+ input_report_key(dev, BTN_MIDDLE, !(data & 2));
+ break;
+
+ case 1:
+ case 3:
+ input_report_rel(dev, REL_X, data / 2);
+ input_report_rel(dev, REL_Y, -buf[1]);
+ buf[0] = data - data / 2;
+ break;
+
+ case 2:
+ case 4:
+ input_report_rel(dev, REL_X, buf[0]);
+ input_report_rel(dev, REL_Y, buf[1] - data);
+ buf[1] = data / 2;
+ break;
+ }
+
+ if (++sermouse->count == (5 - ((sermouse->type == SERIO_SUN) << 1)))
+ sermouse->count = 0;
+}
+
+/*
+ * sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and
+ * generates events. With prediction it gets 80 updates/sec, assuming
+ * standard 3-byte packets and 1200 bps.
+ */
+
+static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
+{
+ struct input_dev *dev = &sermouse->dev;
+ signed char *buf = sermouse->buf;
+
+ if (data & 0x40) sermouse->count = 0;
+
+ switch (sermouse->count) {
+
+ case 0:
+ buf[1] = data;
+ input_report_key(dev, BTN_LEFT, (data >> 5) & 1);
+ input_report_key(dev, BTN_RIGHT, (data >> 4) & 1);
+ break;
+
+ case 1:
+ buf[2] = data;
+ data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f));
+ input_report_rel(dev, REL_X, data / 2);
+ input_report_rel(dev, REL_Y, buf[4]);
+ buf[3] = data - data / 2;
+ break;
+
+ case 2:
+ /* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */
+ if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1]))
+ input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key));
+ buf[0] = buf[1];
+
+ data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f));
+ input_report_rel(dev, REL_X, buf[3]);
+ input_report_rel(dev, REL_Y, data - buf[4]);
+ buf[4] = data / 2;
+ break;
+
+ case 3:
+
+ switch (sermouse->type) {
+
+ case SERIO_MS:
+ sermouse->type = SERIO_MP;
+
+ case SERIO_MP:
+ if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */
+ input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1);
+ input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
+ break;
+
+ case SERIO_MZP:
+ case SERIO_MZPP:
+ input_report_key(dev, BTN_SIDE, (data >> 5) & 1);
+
+ case SERIO_MZ:
+ input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);
+ input_report_rel(dev, REL_WHEEL, (data & 7) - (data & 8));
+ break;
+ }
+
+ break;
+
+ case 4:
+ case 6: /* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */
+ buf[1] = (data >> 2) & 0x0f;
+ break;
+
+ case 5:
+ case 7: /* Ignore anything besides MZ++ */
+ if (sermouse->type != SERIO_MZPP) break;
+
+ switch (buf[1]) {
+
+ case 1: /* Extra mouse info */
+
+ input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
+ input_report_key(dev, BTN_EXTRA, (data >> 5) & 1);
+ input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8));
+
+ break;
+
+ default: /* We don't decode anything else yet. */
+
+ printk(KERN_WARNING
+ "sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]);
+ break;
+ }
+
+ break;
+ }
+
+ sermouse->count++;
+}
+
+/*
+ * sermouse_interrupt() handles incoming characters, either gathering them into
+ * packets or passing them to the command routine as command output.
+ */
+
+static void sermouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct sermouse *sermouse = serio->private;
+
+ if (time_after(jiffies, sermouse->last + HZ/20)) sermouse->count = 0;
+ sermouse->last = jiffies;
+
+ if (sermouse->type > SERIO_SUN)
+ sermouse_process_ms(sermouse, data);
+ else
+ sermouse_process_msc(sermouse, data);
+}
+
+/*
+ * sermouse_disconnect() cleans up after we don't want talk
+ * to the mouse anymore.
+ */
+
+static void sermouse_disconnect(struct serio *serio)
+{
+ struct sermouse *sermouse = serio->private;
+ input_unregister_device(&sermouse->dev);
+ serio_close(serio);
+ kfree(sermouse);
+}
+
+/*
+ * sermouse_connect() is a callback form the serio module when
+ * an unhandled serio port is found.
+ */
+
+static void sermouse_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct sermouse *sermouse;
+ unsigned char c;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_RS232)
+ return;
+
+ if (!(serio->type & SERIO_PROTO) || ((serio->type & SERIO_PROTO) > SERIO_MZPP))
+ return;
+
+ if (!(sermouse = kmalloc(sizeof(struct sermouse), GFP_KERNEL)))
+ return;
+
+ memset(sermouse, 0, sizeof(struct sermouse));
+
+ sermouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ sermouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
+ sermouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ sermouse->dev.private = sermouse;
+
+ serio->private = sermouse;
+
+ sermouse->type = serio->type & SERIO_PROTO;
+ c = (serio->type & SERIO_EXTRA) >> 16;
+
+ if (c & 0x01) set_bit(BTN_MIDDLE, sermouse->dev.keybit);
+ if (c & 0x02) set_bit(BTN_SIDE, sermouse->dev.keybit);
+ if (c & 0x04) set_bit(BTN_EXTRA, sermouse->dev.keybit);
+ if (c & 0x10) set_bit(REL_WHEEL, sermouse->dev.relbit);
+ if (c & 0x20) set_bit(REL_HWHEEL, sermouse->dev.relbit);
+
+ sprintf(sermouse->phys, "%s/input0", serio->phys);
+
+ sermouse->dev.name = sermouse_protocols[sermouse->type];
+ sermouse->dev.phys = sermouse->phys;
+ sermouse->dev.idbus = BUS_RS232;
+ sermouse->dev.idvendor = sermouse->type;
+ sermouse->dev.idproduct = c;
+ sermouse->dev.idversion = 0x0100;
+
+ if (serio_open(serio, dev)) {
+ kfree(sermouse);
+ return;
+ }
+
+ input_register_device(&sermouse->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys);
+}
+
+static struct serio_dev sermouse_dev = {
+ interrupt: sermouse_interrupt,
+ connect: sermouse_connect,
+ disconnect: sermouse_disconnect
+};
+
+int __init sermouse_init(void)
+{
+ serio_register_device(&sermouse_dev);
+ return 0;
+}
+
+void __exit sermouse_exit(void)
+{
+ serio_unregister_device(&sermouse_dev);
+}
+
+module_init(sermouse_init);
+module_exit(sermouse_exit);
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index 01fb98c7f30f..a5b4152e2c97 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -1,5 +1,5 @@
/*
- * $Id: mousedev.c,v 1.38 2001/12/26 21:08:33 jsimmons Exp $
+ * $Id: mousedev.c,v 1.42 2002/04/09 20:51:26 jdeneux Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
@@ -38,6 +38,10 @@
#include <linux/config.h>
#include <linux/smp_lock.h>
#include <linux/random.h>
+#include <linux/major.h>
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+#include <linux/miscdevice.h>
+#endif
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces");
@@ -225,7 +229,14 @@ static int mousedev_release(struct inode * inode, struct file * file)
static int mousedev_open(struct inode * inode, struct file * file)
{
struct mousedev_list *list;
- int i = minor(inode->i_rdev) - MOUSEDEV_MINOR_BASE;
+ int i;
+
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+ if (major(inode->i_rdev) == MISC_MAJOR)
+ i = MOUSEDEV_MIX;
+ else
+#endif
+ i = minor(inode->i_rdev) - MOUSEDEV_MINOR_BASE;
if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
return -ENODEV;
@@ -494,6 +505,12 @@ static struct input_handler mousedev_handler = {
id_table: mousedev_ids,
};
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+static struct miscdevice psaux_mouse = {
+ PSMOUSE_MINOR, "psaux", &mousedev_fops
+};
+#endif
+
static int __init mousedev_init(void)
{
input_register_handler(&mousedev_handler);
@@ -504,6 +521,9 @@ static int __init mousedev_init(void)
mousedev_mix.exist = 1;
mousedev_mix.minor = MOUSEDEV_MIX;
mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE);
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+ misc_register(&psaux_mouse);
+#endif
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
@@ -512,6 +532,9 @@ static int __init mousedev_init(void)
static void __exit mousedev_exit(void)
{
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+ misc_deregister(&psaux_mouse);
+#endif
input_unregister_minor(mousedev_mix.devfs);
input_unregister_handler(&mousedev_handler);
}
diff --git a/drivers/input/power.c b/drivers/input/power.c
new file mode 100644
index 000000000000..84af6b392dd5
--- /dev/null
+++ b/drivers/input/power.c
@@ -0,0 +1,180 @@
+/*
+ * $Id: power.c,v 1.10 2001/09/25 09:17:15 vojtech Exp $
+ *
+ * Copyright (c) 2001 "Crazy" James Simmons
+ *
+ * Input driver Power Management.
+ *
+ * Sponsored by Transvirtual Technology.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <jsimmons@transvirtual.com>.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+
+static struct input_handler power_handler;
+
+/*
+ * Power management can't be done in a interrupt context. So we have to
+ * use keventd.
+ */
+static int suspend_button_pushed = 0;
+static void suspend_button_task_handler(void *data)
+{
+ //extern void pm_do_suspend(void);
+ udelay(200); /* debounce */
+ //pm_do_suspend();
+ suspend_button_pushed = 0;
+}
+
+static struct tq_struct suspend_button_task = {
+ routine: suspend_button_task_handler
+};
+
+static void power_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int down)
+{
+ struct input_dev *dev = handle->dev;
+
+ printk("Entering power_event\n");
+
+ if (type != EV_KEY || type != EV_PWR) return;
+
+ if (type == EV_PWR) {
+ switch (code) {
+ case KEY_SUSPEND:
+ printk("Powering down entire device\n");
+
+ //pm_send_all(PM_SUSPEND, dev);
+
+ if (!suspend_button_pushed) {
+ suspend_button_pushed = 1;
+ schedule_task(&suspend_button_task);
+ }
+ break;
+ case KEY_POWER:
+ /* Hum power down the machine. */
+ break;
+ default:
+ return;
+ }
+ } else {
+ switch (code) {
+ case KEY_SUSPEND:
+ printk("Powering down input device\n");
+ /* This is risky. See pm.h for details. */
+ if (dev->state != PM_RESUME)
+ dev->state = PM_RESUME;
+ else
+ dev->state = PM_SUSPEND;
+ pm_send(dev->pm_dev, dev->state, dev);
+ break;
+ case KEY_POWER:
+ /* Turn the input device off completely ? */
+ break;
+ default:
+ return;
+ }
+ }
+ return;
+}
+
+static struct input_handle *power_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ struct input_device_id *id)
+{
+ struct input_handle *handle;
+
+ if (!test_bit(EV_KEY, dev->evbit) || !test_bit(EV_PWR, dev->evbit))
+ return NULL;
+
+ if (!test_bit(KEY_SUSPEND, dev->keybit) || (!test_bit(KEY_POWER, dev->keybit)))
+ return NULL;
+
+ if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
+ return NULL;
+ memset(handle, 0, sizeof(struct input_handle));
+
+ handle->dev = dev;
+ handle->handler = handler;
+
+ input_open_device(handle);
+
+ printk(KERN_INFO "power.c: Adding power management to input layer\n");
+ return handle;
+}
+
+static void power_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ kfree(handle);
+}
+
+static struct input_device_id power_ids[] = {
+ {
+ flags: INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+ evbit: { BIT(EV_KEY) },
+ keybit: { [LONG(KEY_SUSPEND)] = BIT(KEY_SUSPEND) }
+ },
+ {
+ flags: INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+ evbit: { BIT(EV_KEY) },
+ keybit: { [LONG(KEY_POWER)] = BIT(KEY_POWER) }
+ },
+ {
+ flags: INPUT_DEVICE_ID_MATCH_EVBIT,
+ evbit: { BIT(EV_PWR) },
+ },
+ { }, /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, power_ids);
+
+static struct input_handler power_handler = {
+ event: power_event,
+ connect: power_connect,
+ disconnect: power_disconnect,
+ name: "power",
+ id_table: power_ids,
+};
+
+static int __init power_init(void)
+{
+ input_register_handler(&power_handler);
+ return 0;
+}
+
+static void __exit power_exit(void)
+{
+ input_unregister_handler(&power_handler);
+}
+
+module_init(power_init);
+module_exit(power_exit);
+
+MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
+MODULE_DESCRIPTION("Input Power Management driver");
diff --git a/drivers/input/serio/Config.help b/drivers/input/serio/Config.help
index a82785414452..ae97e4223fc0 100644
--- a/drivers/input/serio/Config.help
+++ b/drivers/input/serio/Config.help
@@ -12,6 +12,18 @@ CONFIG_SERIO
The module will be called serio.o. If you want to compile it
as a module, say M here and read <file:Documentation/modules.txt>.
+CONFIG_SERIO_I8042
+ i8042 is the chip over which the standard AT keyboard and PS/2
+ mouse are connected to the computer. If you use these devices,
+ you'll need to say Y here.
+
+ If unsure, say Y.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called i8042.o. If you want to compile it
+ as a module, say M here and read <file:Documentation/modules.txt>.
+
CONFIG_SERIO_SERPORT
Say Y here if you plan to use an input device (mouse, joystick,
tablet, 6dof) that communicates over the RS232 serial (COM) port.
@@ -24,3 +36,38 @@ CONFIG_SERIO_SERPORT
inserted in and removed from the running kernel whenever you want).
The module will be called serport.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_SERIO_CT82C710
+ Say Y here if you have a Texas Instruments TravelMate notebook
+ equipped with the ct82c710 chip and want to use a mouse connected
+ to the "QuickPort".
+
+ If unsure, say N.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called ct82c710.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_SERIO_PARKBD
+ Say Y here if you built a simple parallel port adapter to attach
+ an additional AT keyboard, XT keyboard or PS/2 mouse.
+
+ More information is available: <file:Documentation/input/input.txt>
+
+ If unsure, say N.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called parkbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_SERIO_ACORN
+ Say Y here if you have the Acorn RiscPC and want to use an AT
+ keyboard connected to its keyboard controller.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called rpckbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
diff --git a/drivers/input/serio/Config.in b/drivers/input/serio/Config.in
index cc566ad20586..e378e810c18e 100644
--- a/drivers/input/serio/Config.in
+++ b/drivers/input/serio/Config.in
@@ -4,4 +4,16 @@
tristate 'Serial i/o support' CONFIG_SERIO
+dep_tristate ' i8042 PC Keyboard controller' CONFIG_SERIO_I8042 $CONFIG_SERIO $CONFIG_ISA
+if [ "$CONFIG_SERIO_I8042" != "n" ]; then
+ hex ' Register Base Address' CONFIG_I8042_REG_BASE 60
+ int ' PS/2 Keyboard IRQ' CONFIG_I8042_KBD_IRQ 1
+ int ' PS/2 AUX IRQ' CONFIG_I8042_AUX_IRQ 12
+fi
dep_tristate ' Serial port line discipline' CONFIG_SERIO_SERPORT $CONFIG_SERIO
+dep_tristate ' ct82c710 Aux port controller' CONFIG_SERIO_CT82C710 $CONFIG_SERIO $CONFIG_ISA
+dep_tristate ' Parallel port keyboard adapter' CONFIG_SERIO_PARKBD $CONFIG_SERIO $CONFIG_PARPORT
+
+if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+ dep_tristate ' Acorn RiscPC keyboard controller' CONFIG_SERIO_ACORN $CONFIG_SERIO
+fi
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 5865d9be9059..8e8e036a0e17 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -9,7 +9,11 @@ export-objs := serio.o
# Each configuration option enables a list of files.
obj-$(CONFIG_SERIO) += serio.o
+obj-$(CONFIG_SERIO_I8042) += i8042.o
+obj-$(CONFIG_SERIO_PARKBD) += parkbd.o
obj-$(CONFIG_SERIO_SERPORT) += serport.o
+obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o
+obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o
# The global Rules.make.
diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c
new file mode 100644
index 000000000000..8202a64606bb
--- /dev/null
+++ b/drivers/input/serio/ct82c710.c
@@ -0,0 +1,212 @@
+/*
+ * $Id: ct82c710.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * 82C710 C&T mouse port chip driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("82C710 C&T mouse port chip driver");
+MODULE_LICENSE("GPL");
+
+static char ct82c710_name[] = "C&T 82c710 mouse port";
+static char ct82c710_phys[16];
+
+/*
+ * ct82c710 interface
+ */
+
+#define CT82C710_DEV_IDLE 0x01 /* Device Idle */
+#define CT82C710_RX_FULL 0x02 /* Device Char received */
+#define CT82C710_TX_IDLE 0x04 /* Device XMIT Idle */
+#define CT82C710_RESET 0x08 /* Device Reset */
+#define CT82C710_INTS_ON 0x10 /* Device Interrupt On */
+#define CT82C710_ERROR_FLAG 0x20 /* Device Error */
+#define CT82C710_CLEAR 0x40 /* Device Clear */
+#define CT82C710_ENABLE 0x80 /* Device Enable */
+
+#define CT82C710_IRQ 12
+
+static int ct82c710_data = 0;
+static int ct82c710_status = 0;
+
+static void ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs);
+
+/*
+ * Wait for device to send output char and flush any input char.
+ */
+
+static int ct82c170_wait(void)
+{
+ int timeout = 60000;
+
+ while ((inb(ct82c710_status) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
+ != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
+
+ if (inb_p(ct82c710_status) & CT82C710_RX_FULL) inb_p(ct82c710_data);
+
+ udelay(1);
+ timeout--;
+ }
+
+ return !timeout;
+}
+
+static void ct82c710_close(struct serio *serio)
+{
+ if (ct82c170_wait())
+ printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
+
+ outb_p(inb_p(ct82c710_status) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), ct82c710_status);
+
+ if (ct82c170_wait())
+ printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
+
+ free_irq(CT82C710_IRQ, NULL);
+}
+
+static int ct82c710_open(struct serio *serio)
+{
+ unsigned char status;
+
+ if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL))
+ return -1;
+
+ status = inb_p(ct82c710_status);
+
+ status |= (CT82C710_ENABLE | CT82C710_RESET);
+ outb_p(status, ct82c710_status);
+
+ status &= ~(CT82C710_RESET);
+ outb_p(status, ct82c710_status);
+
+ status |= CT82C710_INTS_ON;
+ outb_p(status, ct82c710_status); /* Enable interrupts */
+
+ while (ct82c170_wait()) {
+ printk(KERN_ERR "ct82c710: Device busy in open()\n");
+ status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
+ outb_p(status, ct82c710_status);
+ free_irq(CT82C710_IRQ, NULL);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Write to the 82C710 mouse device.
+ */
+
+static int ct82c710_write(struct serio *port, unsigned char c)
+{
+ if (ct82c170_wait()) return -1;
+ outb_p(c, ct82c710_data);
+ return 0;
+}
+
+static struct serio ct82c710_port =
+{
+ type: SERIO_8042,
+ name: ct82c710_name,
+ phys: ct82c710_phys,
+ write: ct82c710_write,
+ open: ct82c710_open,
+ close: ct82c710_close,
+};
+
+/*
+ * Interrupt handler for the 82C710 mouse port. A character
+ * is waiting in the 82C710.
+ */
+
+static void ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
+{
+ if (ct82c710_port.dev)
+ ct82c710_port.dev->interrupt(&ct82c710_port, inb(ct82c710_data), 0);
+}
+
+/*
+ * See if we can find a 82C710 device. Read mouse address.
+ */
+
+static int __init ct82c710_probe(void)
+{
+ outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */
+ outb_p(0xaa, 0x3fa); /* Inverse of 55 */
+ outb_p(0x36, 0x3fa); /* Address the chip */
+ outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */
+ outb_p(0x1b, 0x2fa); /* Inverse of e4 */
+ outb_p(0x0f, 0x390); /* Write index */
+ if (inb_p(0x391) != 0xe4) /* Config address found? */
+ return -1; /* No: no 82C710 here */
+
+ outb_p(0x0d, 0x390); /* Write index */
+ ct82c710_data = inb_p(0x391) << 2; /* Get mouse I/O address */
+ ct82c710_status = ct82c710_data + 1;
+ outb_p(0x0f, 0x390);
+ outb_p(0x0f, 0x391); /* Close config mode */
+
+ return 0;
+}
+
+int __init ct82c710_init(void)
+{
+ if (ct82c710_probe())
+ return -ENODEV;
+
+ if (request_region(ct82c710_data, 2, "ct82c710"))
+ return -EBUSY;
+
+ sprintf(ct82c710_phys, "isa%04x/serio0", ct82c710_data);
+
+ serio_register_port(&ct82c710_port);
+
+ printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n",
+ ct82c710_data, CT82C710_IRQ);
+
+ return 0;
+}
+
+void __exit ct82c710_exit(void)
+{
+ serio_unregister_port(&ct82c710_port);
+ release_region(ct82c710_data, 2);
+}
+
+module_init(ct82c710_init);
+module_exit(ct82c710_exit);
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
new file mode 100644
index 000000000000..5e222b2f2e31
--- /dev/null
+++ b/drivers/input/serio/i8042.c
@@ -0,0 +1,717 @@
+/*
+ * $Id: i8042.c,v 1.21 2002/03/01 22:09:27 jsimmons Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * i8042 keyboard and mouse controller driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/sched.h> /* request/free_irq */
+
+#include "i8042.h"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(i8042_noaux, "1i");
+MODULE_PARM(i8042_unlock, "1i");
+MODULE_PARM(i8042_reset, "1i");
+MODULE_PARM(i8042_direct, "1i");
+
+static int i8042_noaux;
+static int i8042_unlock;
+static int i8042_reset;
+static int i8042_direct;
+
+spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED;
+
+struct i8042_values {
+ int irq;
+ unsigned char disable;
+ unsigned char irqen;
+ unsigned char exists;
+ unsigned char *name;
+ unsigned char *phys;
+};
+
+static struct serio i8042_kbd_port;
+static struct serio i8042_aux_port;
+static unsigned char i8042_initial_ctr;
+static unsigned char i8042_ctr;
+
+#ifdef I8042_DEBUG_IO
+static unsigned long i8042_start;
+#endif
+
+static unsigned long i8042_unxlate_seen[128 / BITS_PER_LONG];
+static unsigned char i8042_unxlate_table[128] = {
+ 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
+ 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
+ 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+};
+
+static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/*
+ * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
+ * be ready for reading values from it / writing values to it.
+ */
+
+static int i8042_wait_read(void)
+{
+ int i = 0;
+ while ((~inb(I8042_STATUS_REG) & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
+ udelay(50);
+ i++;
+ }
+ return -(i == I8042_CTL_TIMEOUT);
+}
+
+static int i8042_wait_write(void)
+{
+ int i = 0;
+ while ((inb(I8042_STATUS_REG) & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
+ udelay(50);
+ i++;
+ }
+ return -(i == I8042_CTL_TIMEOUT);
+}
+
+/*
+ * i8042_flush() flushes all data that may be in the keyboard and mouse buffers
+ * of the i8042 down the toilet.
+ */
+
+static int i8042_flush(void)
+{
+ unsigned long flags;
+ int i = 0;
+
+ spin_lock_irqsave(&i8042_lock, flags);
+
+ while ((inb(I8042_STATUS_REG) & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE))
+#ifdef I8042_DEBUG_IO
+ printk(KERN_DEBUG "i8042.c: %02x <- i8042 (flush) [%d]\n",
+ inb(I8042_DATA_REG), (int) (jiffies - i8042_start));
+#else
+ inb(I8042_DATA_REG);
+#endif
+
+ spin_unlock_irqrestore(&i8042_lock, flags);
+
+ return i;
+}
+
+/*
+ * i8042_command() executes a command on the i8042. It also sends the input parameter(s)
+ * of the commands to it, and receives the output value(s). The parameters are to be
+ * stored in the param array, and the output is placed into the same array. The number
+ * of the parameters and output values is encoded in bits 8-11 of the command
+ * number.
+ */
+
+static int i8042_command(unsigned char *param, int command)
+{
+ unsigned long flags;
+ int retval = 0, i = 0;
+
+ spin_lock_irqsave(&i8042_lock, flags);
+
+ retval = i8042_wait_write();
+ if (!retval) {
+#ifdef I8042_DEBUG_IO
+ printk(KERN_DEBUG "i8042.c: %02x -> i8042 (command) [%d]\n",
+ command & 0xff, (int) (jiffies - i8042_start));
+#endif
+ outb(command & 0xff, I8042_COMMAND_REG);
+ }
+
+ if (!retval)
+ for (i = 0; i < ((command >> 12) & 0xf); i++) {
+ if ((retval = i8042_wait_write())) break;
+#ifdef I8042_DEBUG_IO
+ printk(KERN_DEBUG "i8042.c: %02x -> i8042 (parameter) [%d]\n",
+ param[i], (int) (jiffies - i8042_start));
+#endif
+ outb(param[i], I8042_DATA_REG);
+ }
+
+ if (!retval)
+ for (i = 0; i < ((command >> 8) & 0xf); i++) {
+ if ((retval = i8042_wait_read())) break;
+ if (inb(I8042_STATUS_REG) & I8042_STR_AUXDATA)
+ param[i] = ~inb(I8042_DATA_REG);
+ else
+ param[i] = inb(I8042_DATA_REG);
+#ifdef I8042_DEBUG_IO
+ printk(KERN_DEBUG "i8042.c: %02x <- i8042 (return) [%d]\n",
+ param[i], (int) (jiffies - i8042_start));
+#endif
+ }
+
+ spin_unlock_irqrestore(&i8042_lock, flags);
+
+#ifdef I8042_DEBUG_IO
+ if (retval)
+ printk(KERN_DEBUG "i8042.c: -- i8042 (timeout) [%d]\n",
+ (int) (jiffies - i8042_start));
+#endif
+
+ return retval;
+}
+
+/*
+ * i8042_kbd_write() sends a byte out through the keyboard interface.
+ * It also automatically refreshes the CTR value, since some i8042's
+ * trash their CTR after attempting to send data to an nonexistent
+ * device.
+ */
+
+static int i8042_kbd_write(struct serio *port, unsigned char c)
+{
+ unsigned long flags;
+ int retval = 0;
+
+ spin_lock_irqsave(&i8042_lock, flags);
+
+ if(!(retval = i8042_wait_write())) {
+#ifdef I8042_DEBUG_IO
+ printk(KERN_DEBUG "i8042.c: %02x -> i8042 (kbd-data) [%d]\n",
+ c, (int) (jiffies - i8042_start));
+#endif
+ outb(c, I8042_DATA_REG);
+ }
+
+ spin_unlock_irqrestore(&i8042_lock, flags);
+
+ return retval;
+}
+
+/*
+ * i8042_aux_write() sends a byte out through the aux interface.
+ */
+
+static int i8042_aux_write(struct serio *port, unsigned char c)
+{
+ int retval;
+
+/*
+ * Send the byte out.
+ */
+
+ retval = i8042_command(&c, I8042_CMD_AUX_SEND);
+
+/*
+ * Here we restore the CTR value. I don't know why, but i8042's in half-AT
+ * mode tend to trash their CTR when doing the AUX_SEND command.
+ */
+
+ retval += i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR);
+
+/*
+ * Make sure the interrupt happens and the character is received even
+ * in the case the IRQ isn't wired, so that we can receive further
+ * characters later.
+ */
+
+ i8042_interrupt(0, port, NULL);
+ return retval;
+}
+
+/*
+ * i8042_open() is called when a port is open by the higher layer.
+ * It allocates an interrupt and enables the port.
+ */
+
+static int i8042_open(struct serio *port)
+{
+ struct i8042_values *values = port->driver;
+
+/*
+ * Allocate the interrupt
+ */
+
+ if (request_irq(values->irq, i8042_interrupt, 0, "i8042", NULL)) {
+ printk(KERN_ERR "i8042.c: Can't get irq %d for %s\n", values->irq, values->name);
+ return -1;
+ }
+
+/*
+ * Enable the device and its interrupt.
+ */
+
+ i8042_ctr |= values->irqen;
+ i8042_ctr &= ~values->disable;
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+ printk(KERN_ERR "i8042.c: Can't write CTR while opening %s.\n", values->name);
+ return -1;
+ }
+
+/*
+ * Flush buffers
+ */
+
+ i8042_flush();
+
+ return 0;
+}
+
+/*
+ * i8042_close() frees the interrupt, and disables the interface when the
+ * upper layer doesn't need it anymore.
+ */
+
+static void i8042_close(struct serio *port)
+{
+ struct i8042_values *values = port->driver;
+
+/*
+ * Disable the device and its interrupt.
+ */
+
+ i8042_ctr &= ~values->irqen;
+ i8042_ctr |= values->disable;
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+ printk(KERN_ERR "i8042.c: Can't write CTR while closing %s.\n", values->name);
+ return;
+ }
+
+/*
+ * Free the interrupt
+ */
+
+ free_irq(values->irq, NULL);
+}
+
+/*
+ * Structures for registering the devices in the serio.c module.
+ */
+
+static struct i8042_values i8042_kbd_values = {
+ irq: I8042_KBD_IRQ,
+ irqen: I8042_CTR_KBDINT,
+ disable: I8042_CTR_KBDDIS,
+ name: "KBD",
+ exists: 0,
+};
+
+static struct serio i8042_kbd_port =
+{
+ type: SERIO_8042,
+ write: i8042_kbd_write,
+ open: i8042_open,
+ close: i8042_close,
+ driver: &i8042_kbd_values,
+ name: "i8042 Kbd Port",
+ phys: "isa0060/serio0",
+};
+
+static struct i8042_values i8042_aux_values = {
+ irq: I8042_AUX_IRQ,
+ irqen: I8042_CTR_AUXINT,
+ disable: I8042_CTR_AUXDIS,
+ name: "AUX",
+ exists: 0,
+};
+
+static struct serio i8042_aux_port =
+{
+ type: SERIO_8042,
+ write: i8042_aux_write,
+ open: i8042_open,
+ close: i8042_close,
+ driver: &i8042_aux_values,
+ name: "i8042 Aux Port",
+ phys: "isa0060/serio1",
+};
+
+/*
+ * i8042_interrupt() is the most important function in this driver -
+ * it handles the interrupts from the i8042, and sends incoming bytes
+ * to the upper layers.
+ */
+
+static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ unsigned char str, data;
+
+ spin_lock_irqsave(&i8042_lock, flags);
+
+ while ((str = inb(I8042_STATUS_REG)) & I8042_STR_OBF) {
+
+ data = inb(I8042_DATA_REG);
+
+#ifdef I8042_DEBUG_IO
+ printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt-%s) [%d]\n",
+ data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", (int) (jiffies - i8042_start));
+#endif
+
+ if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {
+ if (i8042_aux_port.dev)
+ i8042_aux_port.dev->interrupt(&i8042_aux_port, data, 0);
+ } else {
+ if (i8042_kbd_values.exists && i8042_kbd_port.dev) {
+ if (!i8042_direct) {
+ if (data > 0x7f) {
+ if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) {
+ i8042_kbd_port.dev->interrupt(&i8042_kbd_port, 0xf0, 0);
+ data = i8042_unxlate_table[data & 0x7f];
+ }
+ } else {
+ set_bit(data, i8042_unxlate_seen);
+ data = i8042_unxlate_table[data];
+ }
+ }
+ i8042_kbd_port.dev->interrupt(&i8042_kbd_port, data, 0);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&i8042_lock, flags);
+}
+
+/*
+ * i8042_controller init initializes the i8042 controller, and,
+ * most importantly, sets it into non-xlated mode.
+ */
+
+static int __init i8042_controller_init(void)
+{
+
+/*
+ * Check the i/o region before we touch it.
+ */
+#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
+ if (check_region(I8042_DATA_REG, 16)) {
+ printk(KERN_ERR "i8042.c: %#x port already in use!\n", I8042_DATA_REG);
+ return -1;
+ }
+#endif
+
+/*
+ * Test the i8042. We need to know if it thinks it's working correctly
+ * before doing anything else.
+ */
+
+ i8042_flush();
+
+ if (i8042_reset) {
+
+ unsigned char param;
+
+ if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
+ printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
+ return -1;
+ }
+
+ if (param != I8042_RET_CTL_TEST) {
+ printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
+ param, I8042_RET_CTL_TEST);
+ return -1;
+ }
+ }
+
+/*
+ * Read the CTR.
+ */
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
+ printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n");
+ return -1;
+ }
+
+/*
+ * Save the CTR for restoral on unload / reboot.
+ */
+
+ i8042_initial_ctr = i8042_ctr;
+
+/*
+ * Disable both interfaces and their interrupts.
+ */
+
+ i8042_ctr |= I8042_CTR_KBDDIS;
+ i8042_ctr &= ~I8042_CTR_KBDINT;
+
+/*
+ * Handle keylock.
+ */
+
+ if (~inb(I8042_STATUS_REG) & I8042_STR_KEYLOCK) {
+
+ if (i8042_unlock) {
+ i8042_ctr |= I8042_CTR_IGNKEYLOCK;
+ } else {
+ printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
+ }
+ }
+
+/*
+ * If the chip is configured into nontranslated mode by the BIOS, don't
+ * bother enabling translating and just use that happily.
+ */
+
+ if (~i8042_ctr & I8042_CTR_XLATE)
+ i8042_direct = 1;
+
+/*
+ * Set nontranslated mode for the kbd interface if requested by an option.
+ * This is vital for a working scancode set 3 support. After this the kbd
+ * interface becomes a simple serial in/out, like the aux interface is. If
+ * the user doesn't wish this, the driver tries to untranslate the values
+ * after the i8042 translates them.
+ */
+
+ if (i8042_direct)
+ i8042_ctr &= ~I8042_CTR_XLATE;
+
+/*
+ * Write CTR back.
+ */
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+ printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Here we try to reset everything back to a state in which the BIOS will be
+ * able to talk to the hardware when rebooting.
+ */
+
+void i8042_controller_cleanup(void)
+{
+
+/*
+ * Reset the controller.
+ */
+
+ if (i8042_reset) {
+ unsigned char param;
+
+ if (i8042_command(&param, I8042_CMD_CTL_TEST))
+ printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n");
+ }
+
+/*
+ * Restore the original control register setting.
+ */
+
+ i8042_ctr = i8042_initial_ctr;
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+ printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
+
+/*
+ * Reset anything that is connected to the ports if the ports
+ * are enabled in the original config.
+ */
+
+ if (i8042_kbd_values.exists)
+ i8042_kbd_write(&i8042_kbd_port, 0xff);
+
+ if (i8042_aux_values.exists)
+ i8042_aux_write(&i8042_aux_port, 0xff);
+}
+
+/*
+ * i8042_check_aux() applies as much paranoia as it can at detecting
+ * the presence of an AUX interface.
+ */
+
+static int __init i8042_check_aux(struct i8042_values *values, struct serio *port)
+{
+ unsigned char param;
+
+ i8042_flush();
+
+/*
+ * Internal loopback test - filters out AT-type i8042's
+ */
+
+ param = 0x5a;
+
+ if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa5)
+ return -1;
+
+/*
+ * External connection test - filters out AT-soldered PS/2 i8042's
+ */
+
+ if (i8042_command(&param, I8042_CMD_AUX_TEST) || param)
+ return -1;
+
+/*
+ * Bit assignment test - filters out PS/2 i8042's in AT mode
+ */
+
+ if (i8042_command(&param, I8042_CMD_AUX_DISABLE))
+ return -1;
+
+ if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS))
+ return -1;
+
+ if (i8042_command(&param, I8042_CMD_AUX_TEST) || param) {
+
+/*
+ * We've got an old AMI i8042 with 'Bad Cache' commands.
+ */
+
+ i8042_command(&param, I8042_CMD_AUX_ENABLE);
+ return -1;
+ }
+
+ if (i8042_command(&param, I8042_CMD_AUX_ENABLE))
+ return -1;
+
+ if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (param & I8042_CTR_AUXDIS))
+ return -1;
+
+/*
+ * Disable the interface.
+ */
+
+ i8042_ctr |= I8042_CTR_AUXDIS;
+ i8042_ctr &= ~I8042_CTR_AUXINT;
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * i8042_port_register() marks the device as existing,
+ * registers it, and reports to the user.
+ */
+
+static int __init i8042_port_register(struct i8042_values *values, struct serio *port)
+{
+ values->exists = 1;
+ serio_register_port(port);
+ printk(KERN_INFO "serio: i8042 %s port at %#x,%#x irq %d\n",
+ values->name, I8042_DATA_REG, I8042_COMMAND_REG, values->irq);
+
+ return 0;
+}
+
+/*
+ * Module init and cleanup functions.
+ */
+
+void __init i8042_setup(char *str, int *ints)
+{
+ if (!strcmp(str, "i8042_reset=1"))
+ i8042_reset = 1;
+ if (!strcmp(str, "i8042_noaux=1"))
+ i8042_noaux = 1;
+ if (!strcmp(str, "i8042_unlock=1"))
+ i8042_unlock = 1;
+ if (!strcmp(str, "i8042_direct=1"))
+ i8042_direct = 1;
+}
+
+/*
+ * Reset the 8042 back to original mode.
+ */
+static int i8042_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code==SYS_DOWN || code==SYS_HALT)
+ i8042_controller_cleanup();
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block i8042_notifier=
+{
+ i8042_notify_sys,
+ NULL,
+ 0
+};
+
+int __init i8042_init(void)
+{
+#ifdef I8042_DEBUG_IO
+ i8042_start = jiffies;
+#endif
+
+ if (i8042_controller_init())
+ return -ENODEV;
+
+ i8042_port_register(&i8042_kbd_values, &i8042_kbd_port);
+
+ if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port))
+ i8042_port_register(&i8042_aux_values, &i8042_aux_port);
+
+/*
+ * On ix86 platforms touching the i8042 data register region can do really
+ * bad things. Because of this the region is always reserved on ix86 boxes.
+ */
+#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
+ request_region(I8042_DATA_REG, 16, "i8042");
+#endif
+ register_reboot_notifier(&i8042_notifier);
+ return 0;
+}
+
+void __exit i8042_exit(void)
+{
+ unregister_reboot_notifier(&i8042_notifier);
+
+ if (i8042_kbd_values.exists)
+ serio_unregister_port(&i8042_kbd_port);
+
+ if (i8042_aux_values.exists)
+ serio_unregister_port(&i8042_aux_port);
+
+ i8042_controller_cleanup();
+#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
+ release_region(I8042_DATA_REG, 16);
+#endif
+}
+
+module_init(i8042_init);
+module_exit(i8042_exit);
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
new file mode 100644
index 000000000000..569f81942f50
--- /dev/null
+++ b/drivers/input/serio/i8042.h
@@ -0,0 +1,128 @@
+#ifndef _I8042_H
+#define _I8042_H
+
+/*
+ * $Id: i8042.h,v 1.6 2001/10/05 22:48:09 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+/*
+ * If you want to reset your i8042 upon boot, define this.
+ */
+
+#undef I8042_RESET
+
+/*
+ * If you want to trace all the i/o the i8042 module does for
+ * debugging purposes, define this.
+ */
+
+#undef I8042_DEBUG_IO
+
+/*
+ * On most PC based systems the keyboard IRQ is 1.
+ */
+
+#define I8042_KBD_IRQ CONFIG_I8042_KBD_IRQ
+
+/*
+ * On most PC based systems the aux port IRQ is 12. There are exceptions,
+ * though. Unfortunately IRQ probing is not possible without touching
+ * the device attached to the port.
+ */
+
+#define I8042_AUX_IRQ CONFIG_I8042_AUX_IRQ
+
+/*
+ * This is in 50us units, the time we wait for the i8042 to react. This
+ * has to be long enough for the i8042 itself to timeout on sending a byte
+ * to a non-existent mouse.
+ */
+
+#define I8042_CTL_TIMEOUT 10000
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG CONFIG_I8042_REG_BASE + 4
+#define I8042_STATUS_REG CONFIG_I8042_REG_BASE + 4
+#define I8042_DATA_REG CONFIG_I8042_REG_BASE
+
+/*
+ * Status register bits.
+ */
+
+#define I8042_STR_PARITY 0x80
+#define I8042_STR_TIMEOUT 0x40
+#define I8042_STR_AUXDATA 0x20
+#define I8042_STR_KEYLOCK 0x10
+#define I8042_STR_CMDDAT 0x08
+#define I8042_STR_IBF 0x02
+#define I8042_STR_OBF 0x01
+
+/*
+ * Control register bits.
+ */
+
+#define I8042_CTR_KBDINT 0x01
+#define I8042_CTR_AUXINT 0x02
+#define I8042_CTR_IGNKEYLOCK 0x08
+#define I8042_CTR_KBDDIS 0x10
+#define I8042_CTR_AUXDIS 0x20
+#define I8042_CTR_XLATE 0x40
+
+/*
+ * Commands.
+ */
+
+#define I8042_CMD_CTL_RCTR 0x0120
+#define I8042_CMD_CTL_WCTR 0x1060
+#define I8042_CMD_CTL_TEST 0x01aa
+
+#define I8042_CMD_KBD_DISABLE 0x00ad
+#define I8042_CMD_KBD_ENABLE 0x00ae
+#define I8042_CMD_KBD_TEST 0x01ab
+#define I8042_CMD_KBD_LOOP 0x11d2
+
+#define I8042_CMD_AUX_DISABLE 0x00a7
+#define I8042_CMD_AUX_ENABLE 0x00a8
+#define I8042_CMD_AUX_TEST 0x01a9
+#define I8042_CMD_AUX_SEND 0x10d4
+#define I8042_CMD_AUX_LOOP 0x11d3
+
+/*
+ * Return codes.
+ */
+
+#define I8042_RET_CTL_TEST 0x55
+
+/*
+ * Expected maximum internal i8042 buffer size. This is used for flushing
+ * the i8042 buffers. 32 should be more than enough.
+ */
+
+#define I8042_BUFFER_SIZE 32
+
+#endif /* _I8042_H */
diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c
new file mode 100644
index 000000000000..18e8c51256f4
--- /dev/null
+++ b/drivers/input/serio/parkbd.c
@@ -0,0 +1,203 @@
+/*
+ * $Id: parkbd.c,v 1.10 2002/03/13 10:09:20 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Parallel port to Keyboard port adapter driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(parkbd, "1i");
+MODULE_PARM(parkbd_mode, "1i");
+
+#define PARKBD_CLOCK 0x01 /* Strobe & Ack */
+#define PARKBD_DATA 0x02 /* AutoFd & Busy */
+
+static int parkbd = 0;
+static int parkbd_mode = SERIO_8042;
+
+static int parkbd_buffer = 0;
+static int parkbd_counter = 0;
+static int parkbd_last = 0;
+static int parkbd_writing = 0;
+static unsigned long parkbd_start = 0;
+
+static struct pardevice *parkbd_dev;
+
+static char parkbd_name[] = "PARKBD AT/XT keyboard adapter";
+static char parkbd_phys[32];
+
+static int parkbd_readlines(void)
+{
+ return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
+}
+
+static void parkbd_writelines(int data)
+{
+ parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
+}
+
+static int parkbd_write(struct serio *port, unsigned char c)
+{
+ unsigned char p;
+
+ if (!parkbd_mode) return -1;
+
+ p = c ^ (c >> 4);
+ p = p ^ (p >> 2);
+ p = p ^ (p >> 1);
+
+ parkbd_counter = 0;
+ parkbd_writing = 1;
+ parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
+
+ parkbd_writelines(2);
+
+ return 0;
+}
+
+static int parkbd_open(struct serio *port)
+{
+ return 0;
+}
+
+static void parkbd_close(struct serio *port)
+{
+}
+
+static struct serio parkbd_port =
+{
+ write: parkbd_write,
+ open: parkbd_open,
+ close: parkbd_close,
+ name: parkbd_name,
+ phys: parkbd_phys,
+};
+
+static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+
+ if (parkbd_writing) {
+
+ if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
+ parkbd_counter = 0;
+ parkbd_buffer = 0;
+ parkbd_writing = 0;
+ parkbd_writelines(3);
+ return;
+ }
+
+ parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
+
+ if (parkbd_counter == 11) {
+ parkbd_counter = 0;
+ parkbd_buffer = 0;
+ parkbd_writing = 0;
+ parkbd_writelines(3);
+ }
+
+ } else {
+
+ if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
+ parkbd_counter = 0;
+ parkbd_buffer = 0;
+ }
+
+ parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
+
+ if (parkbd_counter == parkbd_mode + 10) {
+ if (parkbd_port.dev)
+ parkbd_port.dev->interrupt(&parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0);
+ }
+ }
+
+ parkbd_last = jiffies;
+}
+
+static int parkbd_getport(void)
+{
+ struct parport *pp;
+
+ if (parkbd < 0) {
+ printk(KERN_ERR "parkbd: no port specified\n");
+ return -ENODEV;
+ }
+
+ for (pp = parport_enumerate(); pp != NULL && (parkbd > 0); pp = pp->next) parkbd--;
+
+ if (pp == NULL) {
+ printk(KERN_ERR "parkbd: no such parport\n");
+ return -ENODEV;
+ }
+
+ parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL);
+
+ if (!parkbd_dev)
+ return -ENODEV;
+
+ if (parport_claim(parkbd_dev)) {
+ parport_unregister_device(parkbd_dev);
+ return -EBUSY;
+ }
+
+ parkbd_start = jiffies;
+
+ return 0;
+}
+
+
+int __init parkbd_init(void)
+{
+ if (parkbd_getport()) return -1;
+ parkbd_writelines(3);
+ parkbd_port.type = parkbd_mode;
+
+ sprintf(parkbd_phys, "%s/serio0", parkbd_dev->port->name);
+
+ serio_register_port(&parkbd_port);
+
+ printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
+ parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
+
+ return 0;
+}
+
+void __exit parkbd_exit(void)
+{
+ parport_release(parkbd_dev);
+ serio_unregister_port(&parkbd_port);
+ parport_unregister_device(parkbd_dev);
+}
+
+module_init(parkbd_init);
+module_exit(parkbd_exit);
diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c
new file mode 100644
index 000000000000..a90879b13047
--- /dev/null
+++ b/drivers/input/serio/rpckbd.c
@@ -0,0 +1,117 @@
+/*
+ * $Id: rpckbd.c,v 1.7 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * unknown author
+ */
+
+/*
+ * Acorn RiscPC PS/2 keyboard controller driver for Linux/ARM
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/iomd.h>
+#include <asm/system.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
+MODULE_LICENSE("GPL");
+
+static inline void rpckbd_write(unsigned char val)
+{
+ while(!(inb(IOMD_KCTRL) & (1 << 7)));
+ outb(val, IOMD_KARTTX);
+}
+
+static struct serio rpckbd_port =
+{
+ type: SERIO_8042,
+ write: rpckbd_write,
+ name: "RiscPC PS/2 kbd port",
+ phys: "rpckbd/serio0",
+};
+
+static void rpckbd_rx(int irq, void *dev_id, struct pt_regs *regs)
+{
+ kbd_pt_regs = regs;
+
+ while (inb(IOMD_KCTRL) & (1 << 5))
+ if (rpckbd_port.dev)
+ rpckbd_port.dev->interrupt(&rpckbd_port, inb(IOMD_KARTRX), 0);
+
+}
+
+static void rpckbd_tx(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+static int __init rpckbd_init(void)
+{
+ unsigned long flags;
+
+ /* Reset the keyboard state machine. */
+ outb(0, IOMD_KCTRL);
+ outb(8, IOMD_KCTRL);
+
+ save_flags_cli(flags);
+
+ if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", NULL) != 0) {
+ printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ!\n")
+ return -EBUSY;
+ }
+
+ if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", NULL) != 0) {
+ printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ!\n")
+ free_irq(IRQ_KEYBOARDRX, NULL);
+ return -EBUSY;
+ }
+
+ disable_irq(IRQ_KEYBOARDTX);
+ (void)IOMD_KARTRX;
+
+ restore_flags(flags);
+
+ register_serio_port(&rpckbd_port);
+ printk(KERN_INFO "serio: RiscPC PS/2 kbd port irq %d %d\n", IRQ_KEYBOARDRX, IRQ_KEYBOARDTX);
+
+ return 0;
+}
+
+static void __exit rpckbd_exit(void)
+{
+ free_irq(IRQ_KEYBOARDRX, NULL);
+ free_irq(IRQ_KEYBOARDTX, NULL);
+
+ unregister_serio_port(&rpckbd_port);
+}
+
+module_init(rpckbd_init);
+module_exit(rpckbd_exit);
diff --git a/drivers/input/touchscreen/Config.help b/drivers/input/touchscreen/Config.help
new file mode 100644
index 000000000000..d31ba3326e0a
--- /dev/null
+++ b/drivers/input/touchscreen/Config.help
@@ -0,0 +1,16 @@
+CONFIG_INPUT_TOUCHSCREEN
+ Say Y here, and a list of supported touchscreens will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+CONFIG_TOUCHSCREEN_GUNZE
+ Say Y here if you have the Gunze AHL-51 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called gunze.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
diff --git a/drivers/input/touchscreen/Config.in b/drivers/input/touchscreen/Config.in
new file mode 100644
index 000000000000..f48cbd5e2371
--- /dev/null
+++ b/drivers/input/touchscreen/Config.in
@@ -0,0 +1,7 @@
+#
+# Mouse driver configuration
+#
+
+bool 'Touchscreens' CONFIG_INPUT_TOUCHSCREEN
+
+dep_tristate ' Gunze AHL-51S touchscreen' CONFIG_TOUCHSCREEN_GUNZE $CONFIG_INPUT $CONFIG_INPUT_TOUCHSCREEN $CONFIG_SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
new file mode 100644
index 000000000000..d36ec4de1b15
--- /dev/null
+++ b/drivers/input/touchscreen/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the mouse drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
new file mode 100644
index 000000000000..8ea3050692e9
--- /dev/null
+++ b/drivers/input/touchscreen/gunze.c
@@ -0,0 +1,178 @@
+/*
+ * $Id: gunze.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ */
+
+/*
+ * Gunze AHL-51S touchscreen driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Gunze AHL-51S touchscreen driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define GUNZE_MAX_LENGTH 10
+
+static char *gunze_name = "Gunze AHL-51S TouchScreen";
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct gunze {
+ struct input_dev dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[GUNZE_MAX_LENGTH];
+ char phys[32];
+};
+
+static void gunze_process_packet(struct gunze* gunze)
+{
+ struct input_dev *dev = &gunze->dev;
+
+ if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
+ (gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
+ gunze->data[10] = 0;
+ printk(KERN_WARNING "gunze.c: bad packet: >%s<\n", gunze->data);
+ return;
+ }
+
+ input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10) * 4);
+ input_report_abs(dev, ABS_Y, 3072 - simple_strtoul(gunze->data + 6, NULL, 10) * 3);
+ input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
+}
+
+static void gunze_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct gunze* gunze = serio->private;
+
+ if (data == '\r') {
+ gunze_process_packet(gunze);
+ gunze->idx = 0;
+ } else {
+ if (gunze->idx < GUNZE_MAX_LENGTH)
+ gunze->data[gunze->idx++] = data;
+ }
+}
+
+/*
+ * gunze_disconnect() is the opposite of gunze_connect()
+ */
+
+static void gunze_disconnect(struct serio *serio)
+{
+ struct gunze* gunze = serio->private;
+ input_unregister_device(&gunze->dev);
+ serio_close(serio);
+ kfree(gunze);
+}
+
+/*
+ * gunze_connect() is the routine that is called when someone adds a
+ * new serio device. It looks whether it was registered as a Gunze touchscreen
+ * and if yes, registers it as an input device.
+ */
+
+static void gunze_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct gunze *gunze;
+
+ if (serio->type != (SERIO_RS232 | SERIO_GUNZE))
+ return;
+
+ if (!(gunze = kmalloc(sizeof(struct gunze), GFP_KERNEL)))
+ return;
+
+ memset(gunze, 0, sizeof(struct gunze));
+
+ gunze->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ gunze->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+ gunze->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+ gunze->dev.absmin[ABS_X] = 96; gunze->dev.absmin[ABS_Y] = 72;
+ gunze->dev.absmax[ABS_X] = 4000; gunze->dev.absmax[ABS_Y] = 3000;
+
+ gunze->serio = serio;
+ serio->private = gunze;
+
+ sprintf(gunze->phys, "%s/input0", serio->phys);
+
+ gunze->dev.private = gunze;
+ gunze->dev.name = gunze_name;
+ gunze->dev.phys = gunze->phys;
+ gunze->dev.idbus = BUS_RS232;
+ gunze->dev.idvendor = SERIO_GUNZE;
+ gunze->dev.idproduct = 0x0051;
+ gunze->dev.idversion = 0x0100;
+
+ if (serio_open(serio, dev)) {
+ kfree(gunze);
+ return;
+ }
+
+ input_register_device(&gunze->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", gunze_name, serio->phys);
+}
+
+/*
+ * The serio device structure.
+ */
+
+static struct serio_dev gunze_dev = {
+ interrupt: gunze_interrupt,
+ connect: gunze_connect,
+ disconnect: gunze_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+int __init gunze_init(void)
+{
+ serio_register_device(&gunze_dev);
+ return 0;
+}
+
+void __exit gunze_exit(void)
+{
+ serio_unregister_device(&gunze_dev);
+}
+
+module_init(gunze_init);
+module_exit(gunze_exit);
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c
new file mode 100644
index 000000000000..50d238711aab
--- /dev/null
+++ b/drivers/input/tsdev.c
@@ -0,0 +1,443 @@
+/*
+ * $Id: tsdev.c,v 1.15 2002/04/10 16:50:19 jsimmons Exp $
+ *
+ * Copyright (c) 2001 "Crazy" james Simmons
+ *
+ * Input driver to Touchscreen device driver module.
+ *
+ * Sponsored by Transvirtual Technology
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <jsimmons@transvirtual.com>.
+ */
+
+#define TSDEV_MINOR_BASE 128
+#define TSDEV_MINORS 32
+#define TSDEV_BUFFER_SIZE 64
+
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/smp_lock.h>
+#include <linux/random.h>
+#include <linux/time.h>
+
+#ifndef CONFIG_INPUT_TSDEV_SCREEN_X
+#define CONFIG_INPUT_TSDEV_SCREEN_X 240
+#endif
+#ifndef CONFIG_INPUT_TSDEV_SCREEN_Y
+#define CONFIG_INPUT_TSDEV_SCREEN_Y 320
+#endif
+
+struct tsdev {
+ int exist;
+ int open;
+ int minor;
+ char name[16];
+ wait_queue_head_t wait;
+ struct tsdev_list *list;
+ struct input_handle handle;
+ devfs_handle_t devfs;
+};
+
+/* From Compaq's Touch Screen Specification version 0.2 (draft) */
+typedef struct {
+ short pressure;
+ short x;
+ short y;
+ short millisecs;
+} TS_EVENT;
+
+struct tsdev_list {
+ struct fasync_struct *fasync;
+ struct tsdev_list *next;
+ struct tsdev *tsdev;
+ int head, tail;
+ int oldx, oldy, pendown;
+ TS_EVENT event[TSDEV_BUFFER_SIZE];
+};
+
+static struct input_handler tsdev_handler;
+
+static struct tsdev *tsdev_table[TSDEV_MINORS];
+
+static int xres = CONFIG_INPUT_TSDEV_SCREEN_X;
+static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y;
+
+static int tsdev_fasync(int fd, struct file *file, int on)
+{
+ struct tsdev_list *list = file->private_data;
+ int retval;
+
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+static int tsdev_open(struct inode *inode, struct file *file)
+{
+ int i = minor(inode->i_rdev) - TSDEV_MINOR_BASE;
+ struct tsdev_list *list;
+
+ if (i >= TSDEV_MINORS || !tsdev_table[i])
+ return -ENODEV;
+
+ if (!(list = kmalloc(sizeof(struct tsdev_list), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(list, 0, sizeof(struct tsdev_list));
+
+ list->tsdev = tsdev_table[i];
+ list->next = tsdev_table[i]->list;
+ tsdev_table[i]->list = list;
+ file->private_data = list;
+
+ if (!list->tsdev->open++)
+ if (list->tsdev->exist)
+ input_open_device(&list->tsdev->handle);
+ return 0;
+}
+
+static int tsdev_release(struct inode *inode, struct file *file)
+{
+ struct tsdev_list *list = file->private_data;
+ struct tsdev_list **listptr;
+
+ listptr = &list->tsdev->list;
+ tsdev_fasync(-1, file, 0);
+
+ while (*listptr && (*listptr != list))
+ listptr = &((*listptr)->next);
+ *listptr = (*listptr)->next;
+
+ if (!--list->tsdev->open) {
+ if (list->tsdev->exist) {
+ input_close_device(&list->tsdev->handle);
+ } else {
+ input_unregister_minor(list->tsdev->devfs);
+ tsdev_table[list->tsdev->minor] = NULL;
+ kfree(list->tsdev);
+ }
+ }
+ kfree(list);
+ return 0;
+}
+
+static ssize_t tsdev_read(struct file *file, char *buffer, size_t count,
+ loff_t * ppos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct tsdev_list *list = file->private_data;
+ int retval = 0;
+
+ if (list->head == list->tail) {
+ add_wait_queue(&list->tsdev->wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (list->head == list->tail) {
+ if (!list->tsdev->exist) {
+ retval = -ENODEV;
+ break;
+ }
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&list->tsdev->wait, &wait);
+ }
+
+ if (retval)
+ return retval;
+
+ while (list->head != list->tail
+ && retval + sizeof(TS_EVENT) <= count) {
+ if (copy_to_user
+ (buffer + retval, list->event + list->tail,
+ sizeof(TS_EVENT)))
+ return -EFAULT;
+ list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
+ retval += sizeof(TS_EVENT);
+ }
+ return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int tsdev_poll(struct file *file, poll_table * wait)
+{
+ struct tsdev_list *list = file->private_data;
+
+ poll_wait(file, &list->tsdev->wait, wait);
+ if (list->head != list->tail)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static int tsdev_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+/*
+ struct tsdev_list *list = file->private_data;
+ struct tsdev *evdev = list->tsdev;
+ struct input_dev *dev = tsdev->handle.dev;
+ int retval;
+
+ switch (cmd) {
+ case HHEHE:
+ return 0;
+ case hjff:
+ return 0;
+ default:
+ return 0;
+ }
+*/
+ return -EINVAL;
+}
+
+struct file_operations tsdev_fops = {
+ owner: THIS_MODULE,
+ open: tsdev_open,
+ release: tsdev_release,
+ read: tsdev_read,
+ poll: tsdev_poll,
+ fasync: tsdev_fasync,
+ ioctl: tsdev_ioctl,
+};
+
+static void tsdev_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ struct tsdev *tsdev = handle->private;
+ struct tsdev_list *list = tsdev->list;
+ struct timeval time;
+ int size;
+
+ while (list) {
+ switch (type) {
+ case EV_ABS:
+ switch (code) {
+ case ABS_X:
+ if (!list->pendown)
+ return;
+
+ size =
+ handle->dev->absmax[ABS_X] -
+ handle->dev->absmin[ABS_X];
+ if (size > 0)
+ list->oldx =
+ ((value -
+ handle->dev->absmin[ABS_X]) *
+ xres / size);
+ else
+ list->oldx =
+ ((value -
+ handle->dev->absmin[ABS_X]));
+ break;
+ case ABS_Y:
+ if (!list->pendown)
+ return;
+
+ size =
+ handle->dev->absmax[ABS_Y] -
+ handle->dev->absmin[ABS_Y];
+ if (size > 0)
+ list->oldy =
+ ((value -
+ handle->dev->absmin[ABS_Y]) *
+ yres / size);
+ else
+ list->oldy =
+ ((value -
+ handle->dev->absmin[ABS_Y]));
+ break;
+ case ABS_PRESSURE:
+ list->pendown =
+ ((value >
+ handle->dev->
+ absmin[ABS_PRESSURE])) ? value -
+ handle->dev->absmin[ABS_PRESSURE] : 0;
+ break;
+ }
+ break;
+
+ case EV_REL:
+ switch (code) {
+ case REL_X:
+ if (!list->pendown)
+ return;
+
+ list->oldx += value;
+ if (list->oldx < 0)
+ list->oldx = 0;
+ else if (list->oldx > xres)
+ list->oldx = xres;
+ break;
+ case REL_Y:
+ if (!list->pendown)
+ return;
+
+ list->oldy += value;
+ if (list->oldy < 0)
+ list->oldy = 0;
+ else if (list->oldy > xres)
+ list->oldy = xres;
+ break;
+ }
+ break;
+
+ case EV_KEY:
+ if (code == BTN_TOUCH || code == BTN_MOUSE) {
+ switch (value) {
+ case 0:
+ list->pendown = 0;
+ break;
+ case 1:
+ if (!list->pendown)
+ list->pendown = 1;
+ break;
+ case 2:
+ return;
+ }
+ } else
+ return;
+ break;
+ }
+ do_gettimeofday(&time);
+ list->event[list->head].millisecs = time.tv_usec / 100;
+ list->event[list->head].pressure = list->pendown;
+ list->event[list->head].x = list->oldx;
+ list->event[list->head].y = list->oldy;
+ list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ list = list->next;
+ }
+ wake_up_interruptible(&tsdev->wait);
+}
+
+static struct input_handle *tsdev_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ struct input_device_id *id)
+{
+ struct tsdev *tsdev;
+ int minor;
+
+ for (minor = 0; minor < TSDEV_MINORS && tsdev_table[minor];
+ minor++);
+ if (minor == TSDEV_MINORS) {
+ printk(KERN_ERR
+ "tsdev: You have way too many touchscreens\n");
+ return NULL;
+ }
+
+ if (!(tsdev = kmalloc(sizeof(struct tsdev), GFP_KERNEL)))
+ return NULL;
+ memset(tsdev, 0, sizeof(struct tsdev));
+ init_waitqueue_head(&tsdev->wait);
+
+ tsdev->minor = minor;
+ tsdev_table[minor] = tsdev;
+ sprintf(tsdev->name, "ts%d", minor);
+
+ tsdev->handle.dev = dev;
+ tsdev->handle.name = tsdev->name;
+ tsdev->handle.handler = handler;
+ tsdev->handle.private = tsdev;
+
+ tsdev->devfs =
+ input_register_minor("ts%d", minor, TSDEV_MINOR_BASE);
+
+ tsdev->exist = 1;
+
+ return &tsdev->handle;
+}
+
+static void tsdev_disconnect(struct input_handle *handle)
+{
+ struct tsdev *tsdev = handle->private;
+
+ tsdev->exist = 0;
+
+ if (tsdev->open) {
+ input_close_device(handle);
+ wake_up_interruptible(&tsdev->wait);
+ } else {
+ input_unregister_minor(tsdev->devfs);
+ tsdev_table[tsdev->minor] = NULL;
+ kfree(tsdev);
+ }
+}
+
+static struct input_device_id tsdev_ids[] = {
+ {
+ flags: INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
+ evbit: { BIT(EV_KEY) | BIT(EV_REL) },
+ keybit: { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
+ relbit: { BIT(REL_X) | BIT(REL_Y) },
+ },/* A mouse like device, at least one button, two relative axes */
+
+ {
+ flags: INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+ evbit: { BIT(EV_KEY) | BIT(EV_ABS) },
+ keybit: { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+ absbit: { BIT(ABS_X) | BIT(ABS_Y) },
+ },/* A tablet like device, at least touch detection, two absolute axes */
+
+ {},/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, tsdev_ids);
+
+static struct input_handler tsdev_handler = {
+ event: tsdev_event,
+ connect: tsdev_connect,
+ disconnect: tsdev_disconnect,
+ fops: &tsdev_fops,
+ minor: TSDEV_MINOR_BASE,
+ name: "tsdev",
+ id_table: tsdev_ids,
+};
+
+static int __init tsdev_init(void)
+{
+ input_register_handler(&tsdev_handler);
+ printk(KERN_INFO "ts: Compaq touchscreen protocol output\n");
+ return 0;
+}
+
+static void __exit tsdev_exit(void)
+{
+ input_unregister_handler(&tsdev_handler);
+}
+
+module_init(tsdev_init);
+module_exit(tsdev_exit);
+
+MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
+MODULE_DESCRIPTION("Input driver to touchscreen converter");
+MODULE_PARM(xres, "i");
+MODULE_PARM_DESC(xres, "Horizontal screen resolution");
+MODULE_PARM(yres, "i");
+MODULE_PARM_DESC(yres, "Vertical screen resolution");