aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/input
downloadlinux-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
linux-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Kconfig157
-rw-r--r--drivers/input/Makefile19
-rw-r--r--drivers/input/evbug.c103
-rw-r--r--drivers/input/evdev.c492
-rw-r--r--drivers/input/gameport/Kconfig90
-rw-r--r--drivers/input/gameport/Makefile13
-rw-r--r--drivers/input/gameport/cs461x.c322
-rw-r--r--drivers/input/gameport/emu10k1-gp.c132
-rw-r--r--drivers/input/gameport/fm801-gp.c163
-rw-r--r--drivers/input/gameport/gameport.c797
-rw-r--r--drivers/input/gameport/lightning.c344
-rw-r--r--drivers/input/gameport/ns558.c291
-rw-r--r--drivers/input/gameport/vortex.c186
-rw-r--r--drivers/input/input.c748
-rw-r--r--drivers/input/joydev.c533
-rw-r--r--drivers/input/joystick/Kconfig256
-rw-r--r--drivers/input/joystick/Makefile30
-rw-r--r--drivers/input/joystick/a3d.c417
-rw-r--r--drivers/input/joystick/adi.c560
-rw-r--r--drivers/input/joystick/amijoy.c161
-rw-r--r--drivers/input/joystick/analog.c772
-rw-r--r--drivers/input/joystick/cobra.c264
-rw-r--r--drivers/input/joystick/db9.c647
-rw-r--r--drivers/input/joystick/gamecon.c697
-rw-r--r--drivers/input/joystick/gf2k.c380
-rw-r--r--drivers/input/joystick/grip.c422
-rw-r--r--drivers/input/joystick/grip_mp.c677
-rw-r--r--drivers/input/joystick/guillemot.c289
-rw-r--r--drivers/input/joystick/iforce/Kconfig32
-rw-r--r--drivers/input/joystick/iforce/Makefile20
-rw-r--r--drivers/input/joystick/iforce/iforce-ff.c543
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c557
-rw-r--r--drivers/input/joystick/iforce/iforce-packets.c319
-rw-r--r--drivers/input/joystick/iforce/iforce-serio.c193
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c243
-rw-r--r--drivers/input/joystick/iforce/iforce.h191
-rw-r--r--drivers/input/joystick/interact.c322
-rw-r--r--drivers/input/joystick/joydump.c175
-rw-r--r--drivers/input/joystick/magellan.c247
-rw-r--r--drivers/input/joystick/sidewinder.c807
-rw-r--r--drivers/input/joystick/spaceball.c319
-rw-r--r--drivers/input/joystick/spaceorb.c263
-rw-r--r--drivers/input/joystick/stinger.c236
-rw-r--r--drivers/input/joystick/tmdc.c384
-rw-r--r--drivers/input/joystick/turbografx.c258
-rw-r--r--drivers/input/joystick/twidjoy.c296
-rw-r--r--drivers/input/joystick/warrior.c248
-rw-r--r--drivers/input/keyboard/Kconfig185
-rw-r--r--drivers/input/keyboard/Makefile19
-rw-r--r--drivers/input/keyboard/amikbd.c241
-rw-r--r--drivers/input/keyboard/atkbd.c1148
-rw-r--r--drivers/input/keyboard/corgikbd.c361
-rw-r--r--drivers/input/keyboard/hil_kbd.c375
-rw-r--r--drivers/input/keyboard/hilkbd.c343
-rw-r--r--drivers/input/keyboard/hpps2atkbd.h110
-rw-r--r--drivers/input/keyboard/lkkbd.c752
-rw-r--r--drivers/input/keyboard/locomokbd.c309
-rw-r--r--drivers/input/keyboard/maple_keyb.c190
-rw-r--r--drivers/input/keyboard/newtonkbd.c184
-rw-r--r--drivers/input/keyboard/sunkbd.c353
-rw-r--r--drivers/input/keyboard/xtkbd.c188
-rw-r--r--drivers/input/misc/Kconfig60
-rw-r--r--drivers/input/misc/Makefile12
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c724
-rw-r--r--drivers/input/misc/m68kspkr.c83
-rw-r--r--drivers/input/misc/pcspkr.c97
-rw-r--r--drivers/input/misc/sparcspkr.c189
-rw-r--r--drivers/input/misc/uinput.c620
-rw-r--r--drivers/input/mouse/Kconfig138
-rw-r--r--drivers/input/mouse/Makefile18
-rw-r--r--drivers/input/mouse/alps.c477
-rw-r--r--drivers/input/mouse/alps.h32
-rw-r--r--drivers/input/mouse/amimouse.c137
-rw-r--r--drivers/input/mouse/hil_ptr.c414
-rw-r--r--drivers/input/mouse/inport.c196
-rw-r--r--drivers/input/mouse/logibm.c183
-rw-r--r--drivers/input/mouse/logips2pp.c397
-rw-r--r--drivers/input/mouse/logips2pp.h16
-rw-r--r--drivers/input/mouse/maplemouse.c134
-rw-r--r--drivers/input/mouse/pc110pad.c178
-rw-r--r--drivers/input/mouse/psmouse-base.c1011
-rw-r--r--drivers/input/mouse/psmouse.h106
-rw-r--r--drivers/input/mouse/rpcmouse.c107
-rw-r--r--drivers/input/mouse/sermouse.c370
-rw-r--r--drivers/input/mouse/synaptics.c700
-rw-r--r--drivers/input/mouse/synaptics.h110
-rw-r--r--drivers/input/mouse/vsxxxaa.c591
-rw-r--r--drivers/input/mousedev.c758
-rw-r--r--drivers/input/power.c169
-rw-r--r--drivers/input/serio/Kconfig183
-rw-r--r--drivers/input/serio/Makefile23
-rw-r--r--drivers/input/serio/ambakmi.c231
-rw-r--r--drivers/input/serio/ct82c710.c225
-rw-r--r--drivers/input/serio/gscps2.c467
-rw-r--r--drivers/input/serio/hil_mlc.c949
-rw-r--r--drivers/input/serio/hp_sdc.c1054
-rw-r--r--drivers/input/serio/hp_sdc_mlc.c358
-rw-r--r--drivers/input/serio/i8042-io.h93
-rw-r--r--drivers/input/serio/i8042-ip22io.h76
-rw-r--r--drivers/input/serio/i8042-jazzio.h69
-rw-r--r--drivers/input/serio/i8042-ppcio.h136
-rw-r--r--drivers/input/serio/i8042-sparcio.h116
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h333
-rw-r--r--drivers/input/serio/i8042.c1116
-rw-r--r--drivers/input/serio/i8042.h133
-rw-r--r--drivers/input/serio/libps2.c305
-rw-r--r--drivers/input/serio/maceps2.c176
-rw-r--r--drivers/input/serio/parkbd.c218
-rw-r--r--drivers/input/serio/pcips2.c234
-rw-r--r--drivers/input/serio/q40kbd.c163
-rw-r--r--drivers/input/serio/rpckbd.c156
-rw-r--r--drivers/input/serio/sa1111ps2.c359
-rw-r--r--drivers/input/serio/serio.c859
-rw-r--r--drivers/input/serio/serio_raw.c403
-rw-r--r--drivers/input/serio/serport.c226
-rw-r--r--drivers/input/touchscreen/Kconfig98
-rw-r--r--drivers/input/touchscreen/Makefile13
-rw-r--r--drivers/input/touchscreen/corgi_ts.c380
-rw-r--r--drivers/input/touchscreen/elo.c315
-rw-r--r--drivers/input/touchscreen/gunze.c205
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c528
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c135
-rw-r--r--drivers/input/touchscreen/mk712.c222
-rw-r--r--drivers/input/touchscreen/mtouch.c219
-rw-r--r--drivers/input/tsdev.c492
125 files changed, 39668 insertions, 0 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
new file mode 100644
index 000000000000..58223b5d842a
--- /dev/null
+++ b/drivers/input/Kconfig
@@ -0,0 +1,157 @@
+#
+# Input device configuration
+#
+
+menu "Input device support"
+
+config INPUT
+ tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED
+ default y
+ ---help---
+ Say Y here if you have any input device (mouse, keyboard, tablet,
+ joystick, steering wheel ...) connected to your system and want
+ it to be available to applications. This includes standard PS/2
+ keyboard and mouse.
+
+ Say N here if you have a headless (no monitor, no keyboard) system.
+
+ More information is available: <file:Documentation/input/input.txt>
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called input.
+
+if INPUT
+
+comment "Userland interfaces"
+
+config INPUT_MOUSEDEV
+ tristate "Mouse interface" if EMBEDDED
+ default y
+ ---help---
+ Say Y here if you want your mouse to be accessible as char devices
+ 13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an
+ emulated IntelliMouse Explorer PS/2 mouse. That way, all user space
+ programs (including SVGAlib, GPM and X) will be able to use your
+ mouse.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mousedev.
+
+config INPUT_MOUSEDEV_PSAUX
+ bool "Provide legacy /dev/psaux device"
+ default y
+ depends on INPUT_MOUSEDEV
+ ---help---
+ Say Y here if you want your mouse also be accessible as char device
+ 10:1 - /dev/psaux. The data available through /dev/psaux is exactly
+ the same as the data from /dev/input/mice.
+
+ If unsure, say Y.
+
+
+config INPUT_MOUSEDEV_SCREEN_X
+ int "Horizontal screen resolution"
+ depends on INPUT_MOUSEDEV
+ default "1024"
+ help
+ If you're using a digitizer, or a graphic tablet, and want to use
+ it as a mouse then the mousedev driver needs to know the X window
+ screen resolution you are using to correctly scale the data. If
+ you're not using a digitizer, this value is ignored.
+
+config INPUT_MOUSEDEV_SCREEN_Y
+ int "Vertical screen resolution"
+ depends on INPUT_MOUSEDEV
+ default "768"
+ help
+ If you're using a digitizer, or a graphic tablet, and want to use
+ it as a mouse then the mousedev driver needs to know the X window
+ screen resolution you are using to correctly scale the data. If
+ you're not using a digitizer, this value is ignored.
+
+config INPUT_JOYDEV
+ tristate "Joystick interface"
+ ---help---
+ Say Y here if you want your joystick or gamepad to be
+ accessible as char device 13:0+ - /dev/input/jsX device.
+
+ If unsure, say Y.
+
+ More information is available: <file:Documentation/input/joystick.txt>
+
+ To compile this driver as a module, choose M here: the
+ module will be called joydev.
+
+config INPUT_TSDEV
+ tristate "Touchscreen interface"
+ ---help---
+ 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.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsdev.
+
+config INPUT_TSDEV_SCREEN_X
+ int "Horizontal screen resolution"
+ depends on INPUT_TSDEV
+ default "240"
+
+config INPUT_TSDEV_SCREEN_Y
+ int "Vertical screen resolution"
+ depends on INPUT_TSDEV
+ default "320"
+
+config INPUT_EVDEV
+ tristate "Event interface"
+ help
+ Say Y here if you want your input device events be accessible
+ under char device 13:64+ - /dev/input/eventX in a generic way.
+
+ To compile this driver as a module, choose M here: the
+ module will be called evdev.
+
+config INPUT_EVBUG
+ tristate "Event debugging"
+ ---help---
+ 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.
+
+ To compile this driver as a module, choose M here: the
+ module will be called evbug.
+
+comment "Input Device Drivers"
+
+source "drivers/input/keyboard/Kconfig"
+
+source "drivers/input/mouse/Kconfig"
+
+source "drivers/input/joystick/Kconfig"
+
+source "drivers/input/touchscreen/Kconfig"
+
+source "drivers/input/misc/Kconfig"
+
+endif
+
+menu "Hardware I/O ports"
+
+source "drivers/input/serio/Kconfig"
+
+source "drivers/input/gameport/Kconfig"
+
+endmenu
+
+endmenu
+
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
new file mode 100644
index 000000000000..1a6ff4982f30
--- /dev/null
+++ b/drivers/input/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT) += input.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/
+obj-$(CONFIG_INPUT_MISC) += misc/
diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c
new file mode 100644
index 000000000000..d7828936fd8f
--- /dev/null
+++ b/drivers/input/evbug.c
@@ -0,0 +1,103 @@
+/*
+ * $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>
+#include <linux/device.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input driver event debug module");
+MODULE_LICENSE("GPL");
+
+static char evbug_name[] = "evbug";
+
+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;
+ handle->name = evbug_name;
+
+ 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,
+};
+
+static int __init evbug_init(void)
+{
+ input_register_handler(&evbug_handler);
+ return 0;
+}
+
+static 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
new file mode 100644
index 000000000000..17552a29978b
--- /dev/null
+++ b/drivers/input/evdev.c
@@ -0,0 +1,492 @@
+/*
+ * Event char devices, giving access to raw input device events.
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define EVDEV_MINOR_BASE 64
+#define EVDEV_MINORS 32
+#define EVDEV_BUFFER_SIZE 64
+
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/major.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+
+struct evdev {
+ int exist;
+ int open;
+ int minor;
+ char name[16];
+ struct input_handle handle;
+ wait_queue_head_t wait;
+ struct evdev_list *grab;
+ struct list_head list;
+};
+
+struct evdev_list {
+ struct input_event buffer[EVDEV_BUFFER_SIZE];
+ int head;
+ int tail;
+ struct fasync_struct *fasync;
+ struct evdev *evdev;
+ struct list_head node;
+};
+
+static struct evdev *evdev_table[EVDEV_MINORS];
+
+static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+ struct evdev *evdev = handle->private;
+ struct evdev_list *list;
+
+ if (evdev->grab) {
+ list = evdev->grab;
+
+ do_gettimeofday(&list->buffer[list->head].time);
+ list->buffer[list->head].type = type;
+ list->buffer[list->head].code = code;
+ list->buffer[list->head].value = value;
+ list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ } else
+ list_for_each_entry(list, &evdev->list, node) {
+
+ do_gettimeofday(&list->buffer[list->head].time);
+ list->buffer[list->head].type = type;
+ list->buffer[list->head].code = code;
+ list->buffer[list->head].value = value;
+ list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ }
+
+ wake_up_interruptible(&evdev->wait);
+}
+
+static int evdev_fasync(int fd, struct file *file, int on)
+{
+ int retval;
+ struct evdev_list *list = file->private_data;
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+static int evdev_flush(struct file * file)
+{
+ struct evdev_list *list = file->private_data;
+ if (!list->evdev->exist) return -ENODEV;
+ return input_flush_device(&list->evdev->handle, file);
+}
+
+static void evdev_free(struct evdev *evdev)
+{
+ evdev_table[evdev->minor] = NULL;
+ kfree(evdev);
+}
+
+static int evdev_release(struct inode * inode, struct file * file)
+{
+ struct evdev_list *list = file->private_data;
+
+ if (list->evdev->grab == list) {
+ input_release_device(&list->evdev->handle);
+ list->evdev->grab = NULL;
+ }
+
+ evdev_fasync(-1, file, 0);
+ list_del(&list->node);
+
+ if (!--list->evdev->open) {
+ if (list->evdev->exist)
+ input_close_device(&list->evdev->handle);
+ else
+ evdev_free(list->evdev);
+ }
+
+ kfree(list);
+ return 0;
+}
+
+static int evdev_open(struct inode * inode, struct file * file)
+{
+ struct evdev_list *list;
+ int i = iminor(inode) - EVDEV_MINOR_BASE;
+ int accept_err;
+
+ if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist)
+ return -ENODEV;
+
+ if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file)))
+ return accept_err;
+
+ if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(list, 0, sizeof(struct evdev_list));
+
+ list->evdev = evdev_table[i];
+ list_add_tail(&list->node, &evdev_table[i]->list);
+ file->private_data = list;
+
+ if (!list->evdev->open++)
+ if (list->evdev->exist)
+ input_open_device(&list->evdev->handle);
+
+ return 0;
+}
+
+static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+{
+ struct evdev_list *list = file->private_data;
+ 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)))
+ return -EFAULT;
+ input_event(list->evdev->handle.dev, event.type, event.code, event.value);
+ retval += sizeof(struct input_event);
+ }
+
+ return retval;
+}
+
+static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
+{
+ struct evdev_list *list = file->private_data;
+ int retval;
+
+ if (count < sizeof(struct input_event))
+ return -EINVAL;
+
+ if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(list->evdev->wait,
+ list->head != list->tail || (!list->evdev->exist));
+
+ if (retval)
+ return retval;
+
+ if (!list->evdev->exist)
+ return -ENODEV;
+
+ while (list->head != list->tail && retval + sizeof(struct input_event) <= count) {
+ if (copy_to_user(buffer + retval, list->buffer + list->tail,
+ sizeof(struct input_event))) return -EFAULT;
+ list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
+ retval += sizeof(struct input_event);
+ }
+
+ return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int evdev_poll(struct file *file, poll_table *wait)
+{
+ struct evdev_list *list = file->private_data;
+ poll_wait(file, &list->evdev->wait, wait);
+ return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
+ (list->evdev->exist ? 0 : (POLLHUP | POLLERR));
+}
+
+static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct evdev_list *list = file->private_data;
+ struct evdev *evdev = list->evdev;
+ struct input_dev *dev = evdev->handle.dev;
+ struct input_absinfo abs;
+ void __user *p = (void __user *)arg;
+ int __user *ip = (int __user *)arg;
+ int i, t, u, v;
+
+ if (!evdev->exist) return -ENODEV;
+
+ switch (cmd) {
+
+ case EVIOCGVERSION:
+ return put_user(EV_VERSION, ip);
+
+ case EVIOCGID:
+ return copy_to_user(p, &dev->id, sizeof(struct input_id)) ? -EFAULT : 0;
+
+ case EVIOCGKEYCODE:
+ if (get_user(t, ip)) return -EFAULT;
+ if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) return -EINVAL;
+ if (put_user(INPUT_KEYCODE(dev, t), ip + 1)) return -EFAULT;
+ return 0;
+
+ case EVIOCSKEYCODE:
+ if (get_user(t, ip)) return -EFAULT;
+ if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) return -EINVAL;
+ if (get_user(v, ip + 1)) return -EFAULT;
+ if (v < 0 || v > KEY_MAX) return -EINVAL;
+ u = SET_INPUT_KEYCODE(dev, t, v);
+ clear_bit(u, dev->keybit);
+ set_bit(v, dev->keybit);
+ for (i = 0; i < dev->keycodemax; i++)
+ if (INPUT_KEYCODE(dev,i) == u)
+ set_bit(u, dev->keybit);
+ return 0;
+
+ case EVIOCSFF:
+ if (dev->upload_effect) {
+ struct ff_effect effect;
+ int err;
+
+ if (copy_from_user(&effect, p, sizeof(effect)))
+ return -EFAULT;
+ err = dev->upload_effect(dev, &effect);
+ if (put_user(effect.id, &(((struct ff_effect __user *)arg)->id)))
+ return -EFAULT;
+ return err;
+ }
+ else return -ENOSYS;
+
+ case EVIOCRMFF:
+ if (dev->erase_effect) {
+ return dev->erase_effect(dev, (int)arg);
+ }
+ else return -ENOSYS;
+
+ case EVIOCGEFFECTS:
+ if (put_user(dev->ff_effects_max, ip))
+ return -EFAULT;
+ return 0;
+
+ case EVIOCGRAB:
+ if (arg) {
+ if (evdev->grab)
+ return -EBUSY;
+ if (input_grab_device(&evdev->handle))
+ return -EBUSY;
+ evdev->grab = list;
+ return 0;
+ } else {
+ if (evdev->grab != list)
+ return -EINVAL;
+ input_release_device(&evdev->handle);
+ evdev->grab = NULL;
+ return 0;
+ }
+
+ default:
+
+ if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ)
+ return -EINVAL;
+
+ if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
+
+ long *bits;
+ int len;
+
+ switch (_IOC_NR(cmd) & EV_MAX) {
+ case 0: bits = dev->evbit; len = EV_MAX; break;
+ case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
+ case EV_REL: bits = dev->relbit; len = REL_MAX; break;
+ case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+ case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
+ case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+ case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+ case EV_FF: bits = dev->ffbit; len = FF_MAX; break;
+ default: return -EINVAL;
+ }
+ len = NBITS(len) * sizeof(long);
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user(p, bits, len) ? -EFAULT : len;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) {
+ int len;
+ len = NBITS(KEY_MAX) * sizeof(long);
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user(p, dev->key, len) ? -EFAULT : len;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) {
+ int len;
+ len = NBITS(LED_MAX) * sizeof(long);
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user(p, dev->led, len) ? -EFAULT : len;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) {
+ int len;
+ len = NBITS(SND_MAX) * sizeof(long);
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user(p, dev->snd, len) ? -EFAULT : len;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
+ int len;
+ if (!dev->name) return -ENOENT;
+ len = strlen(dev->name) + 1;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user(p, dev->name, len) ? -EFAULT : len;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) {
+ int len;
+ if (!dev->phys) return -ENOENT;
+ len = strlen(dev->phys) + 1;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user(p, dev->phys, len) ? -EFAULT : len;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) {
+ int len;
+ if (!dev->uniq) return -ENOENT;
+ len = strlen(dev->uniq) + 1;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user(p, dev->uniq, len) ? -EFAULT : len;
+ }
+
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+
+ int t = _IOC_NR(cmd) & ABS_MAX;
+
+ abs.value = dev->abs[t];
+ abs.minimum = dev->absmin[t];
+ abs.maximum = dev->absmax[t];
+ abs.fuzz = dev->absfuzz[t];
+ abs.flat = dev->absflat[t];
+
+ if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+
+ int t = _IOC_NR(cmd) & ABS_MAX;
+
+ if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
+ return -EFAULT;
+
+ dev->abs[t] = abs.value;
+ dev->absmin[t] = abs.minimum;
+ dev->absmax[t] = abs.maximum;
+ dev->absfuzz[t] = abs.fuzz;
+ dev->absflat[t] = abs.flat;
+
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct file_operations evdev_fops = {
+ .owner = THIS_MODULE,
+ .read = evdev_read,
+ .write = evdev_write,
+ .poll = evdev_poll,
+ .open = evdev_open,
+ .release = evdev_release,
+ .ioctl = evdev_ioctl,
+ .fasync = evdev_fasync,
+ .flush = evdev_flush
+};
+
+static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+{
+ struct evdev *evdev;
+ int minor;
+
+ for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
+ if (minor == EVDEV_MINORS) {
+ printk(KERN_ERR "evdev: no more free evdev devices\n");
+ return NULL;
+ }
+
+ if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
+ return NULL;
+ memset(evdev, 0, sizeof(struct evdev));
+
+ INIT_LIST_HEAD(&evdev->list);
+ init_waitqueue_head(&evdev->wait);
+
+ evdev->exist = 1;
+ evdev->minor = minor;
+ evdev->handle.dev = dev;
+ evdev->handle.name = evdev->name;
+ evdev->handle.handler = handler;
+ evdev->handle.private = evdev;
+ sprintf(evdev->name, "event%d", minor);
+
+ evdev_table[minor] = evdev;
+
+ devfs_mk_cdev(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
+ S_IFCHR|S_IRUGO|S_IWUSR, "input/event%d", minor);
+ class_simple_device_add(input_class,
+ MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
+ dev->dev, "event%d", minor);
+
+ return &evdev->handle;
+}
+
+static void evdev_disconnect(struct input_handle *handle)
+{
+ struct evdev *evdev = handle->private;
+ struct evdev_list *list;
+
+ class_simple_device_remove(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor));
+ devfs_remove("input/event%d", evdev->minor);
+ evdev->exist = 0;
+
+ if (evdev->open) {
+ input_close_device(handle);
+ wake_up_interruptible(&evdev->wait);
+ list_for_each_entry(list, &evdev->list, node)
+ kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+ } else
+ evdev_free(evdev);
+}
+
+static struct input_device_id evdev_ids[] = {
+ { .driver_info = 1 }, /* Matches all devices */
+ { }, /* Terminating zero entry */
+};
+
+MODULE_DEVICE_TABLE(input, evdev_ids);
+
+static struct input_handler evdev_handler = {
+ .event = evdev_event,
+ .connect = evdev_connect,
+ .disconnect = evdev_disconnect,
+ .fops = &evdev_fops,
+ .minor = EVDEV_MINOR_BASE,
+ .name = "evdev",
+ .id_table = evdev_ids,
+};
+
+static int __init evdev_init(void)
+{
+ input_register_handler(&evdev_handler);
+ return 0;
+}
+
+static void __exit evdev_exit(void)
+{
+ input_unregister_handler(&evdev_handler);
+}
+
+module_init(evdev_init);
+module_exit(evdev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input driver event char devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/gameport/Kconfig b/drivers/input/gameport/Kconfig
new file mode 100644
index 000000000000..6282f460aba0
--- /dev/null
+++ b/drivers/input/gameport/Kconfig
@@ -0,0 +1,90 @@
+#
+# Gameport configuration
+#
+config GAMEPORT
+ tristate "Gameport support"
+ ---help---
+ Gameport support is for the standard 15-pin PC gameport. If you
+ have a joystick, gamepad, gameport card, a soundcard with a gameport
+ or anything else that uses the gameport, say Y or M here and also to
+ at least one of the hardware specific drivers.
+
+ For Ensoniq AudioPCI (ES1370), AudioPCI 97 (ES1371), ESS Solo1,
+ S3 SonicVibes, Trident 4DWave, SiS7018, and ALi 5451 gameport
+ support is provided by the sound drivers, so you won't need any
+ from the below listed modules. You still need to say Y here.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gameport.
+
+if GAMEPORT
+
+config GAMEPORT_NS558
+ tristate "Classic ISA and PnP gameport support"
+ help
+ Say Y here if you have an ISA or PnP gameport.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ns558.
+
+config GAMEPORT_L4
+ tristate "PDPI Lightning 4 gamecard support"
+ help
+ Say Y here if you have a PDPI Lightning 4 gamecard.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lightning.
+
+config GAMEPORT_EMU10K1
+ tristate "SB Live and Audigy gameport support"
+ depends on PCI
+ help
+ Say Y here if you have a SoundBlaster Live! or SoundBlaster
+ Audigy card and want to use its gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called emu10k1-gp.
+
+config GAMEPORT_VORTEX
+ tristate "Aureal Vortex, Vortex 2 gameport support"
+ depends on PCI
+ help
+ Say Y here if you have an Aureal Vortex 1 or 2 card and want
+ to use its gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vortex.
+
+config GAMEPORT_FM801
+ tristate "ForteMedia FM801 gameport support"
+ depends on PCI
+
+config GAMEPORT_CS461X
+ tristate "Crystal SoundFusion gameport support"
+ depends on PCI
+
+endif
+
+# Yes, SOUND_GAMEPORT looks a bit odd. Yes, it ends up being turned on
+# in every .config. Please don't touch it. It is here to handle an
+# unusual dependency between GAMEPORT and sound drivers.
+#
+# Some sound drivers call gameport functions. If GAMEPORT is
+# not selected, empty stubs are provided for the functions and all is
+# well.
+# If GAMEPORT is built in, everything is fine.
+# If GAMEPORT is a module, however, it would need to be loaded for the
+# sound driver to be able to link properly. Therefore, the sound
+# driver must be a module as well in that case. Since there's no way
+# to express that directly in Kconfig, we use SOUND_GAMEPORT to
+# express it. SOUND_GAMEPORT boils down to "if GAMEPORT is 'm',
+# anything that depends on SOUND_GAMEPORT must be 'm' as well. if
+# GAMEPORT is 'y' or 'n', it can be anything".
+config SOUND_GAMEPORT
+ tristate
+ default m if GAMEPORT=m
+ default y
diff --git a/drivers/input/gameport/Makefile b/drivers/input/gameport/Makefile
new file mode 100644
index 000000000000..5367b4267adf
--- /dev/null
+++ b/drivers/input/gameport/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the gameport drivers.
+#
+
+# Each configuration option enables a list of files.
+
+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_VORTEX) += vortex.o
diff --git a/drivers/input/gameport/cs461x.c b/drivers/input/gameport/cs461x.c
new file mode 100644
index 000000000000..d4013ff98623
--- /dev/null
+++ b/drivers/input/gameport/cs461x.c
@@ -0,0 +1,322 @@
+/*
+ The all defines and part of code (such as cs461x_*) are
+ contributed from ALSA 0.5.8 sources.
+ See http://www.alsa-project.org/ for sources
+
+ Tested on Linux 686 2.4.0-test9, ALSA 0.5.8a and CS4610
+*/
+
+#include <asm/io.h>
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+MODULE_AUTHOR("Victor Krapivin");
+MODULE_LICENSE("GPL");
+
+/*
+ These options are experimental
+
+#define CS461X_FULL_MAP
+*/
+
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4610
+#define PCI_DEVICE_ID_CIRRUS_4610 0x6001
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4612
+#define PCI_DEVICE_ID_CIRRUS_4612 0x6003
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4615
+#define PCI_DEVICE_ID_CIRRUS_4615 0x6004
+#endif
+
+/* Registers */
+
+#define BA0_JSPT 0x00000480
+#define BA0_JSCTL 0x00000484
+#define BA0_JSC1 0x00000488
+#define BA0_JSC2 0x0000048C
+#define BA0_JSIO 0x000004A0
+
+/* Bits for JSPT */
+
+#define JSPT_CAX 0x00000001
+#define JSPT_CAY 0x00000002
+#define JSPT_CBX 0x00000004
+#define JSPT_CBY 0x00000008
+#define JSPT_BA1 0x00000010
+#define JSPT_BA2 0x00000020
+#define JSPT_BB1 0x00000040
+#define JSPT_BB2 0x00000080
+
+/* Bits for JSCTL */
+
+#define JSCTL_SP_MASK 0x00000003
+#define JSCTL_SP_SLOW 0x00000000
+#define JSCTL_SP_MEDIUM_SLOW 0x00000001
+#define JSCTL_SP_MEDIUM_FAST 0x00000002
+#define JSCTL_SP_FAST 0x00000003
+#define JSCTL_ARE 0x00000004
+
+/* Data register pairs masks */
+
+#define JSC1_Y1V_MASK 0x0000FFFF
+#define JSC1_X1V_MASK 0xFFFF0000
+#define JSC1_Y1V_SHIFT 0
+#define JSC1_X1V_SHIFT 16
+#define JSC2_Y2V_MASK 0x0000FFFF
+#define JSC2_X2V_MASK 0xFFFF0000
+#define JSC2_Y2V_SHIFT 0
+#define JSC2_X2V_SHIFT 16
+
+/* JS GPIO */
+
+#define JSIO_DAX 0x00000001
+#define JSIO_DAY 0x00000002
+#define JSIO_DBX 0x00000004
+#define JSIO_DBY 0x00000008
+#define JSIO_AXOE 0x00000010
+#define JSIO_AYOE 0x00000020
+#define JSIO_BXOE 0x00000040
+#define JSIO_BYOE 0x00000080
+
+/*
+ The card initialization code is obfuscated; the module cs461x
+ need to be loaded after ALSA modules initialized and something
+ played on the CS 4610 chip (see sources for details of CS4610
+ initialization code from ALSA)
+*/
+
+/* Card specific definitions */
+
+#define CS461X_BA0_SIZE 0x2000
+#define CS461X_BA1_DATA0_SIZE 0x3000
+#define CS461X_BA1_DATA1_SIZE 0x3800
+#define CS461X_BA1_PRG_SIZE 0x7000
+#define CS461X_BA1_REG_SIZE 0x0100
+
+#define BA1_SP_DMEM0 0x00000000
+#define BA1_SP_DMEM1 0x00010000
+#define BA1_SP_PMEM 0x00020000
+#define BA1_SP_REG 0x00030000
+
+#define BA1_DWORD_SIZE (13 * 1024 + 512)
+#define BA1_MEMORY_COUNT 3
+
+/*
+ Only one CS461x card is still suppoted; the code requires
+ redesign to avoid this limitatuion.
+*/
+
+static unsigned long ba0_addr;
+static unsigned int __iomem *ba0;
+
+#ifdef CS461X_FULL_MAP
+static unsigned long ba1_addr;
+static union ba1_t {
+ struct {
+ unsigned int __iomem *data0;
+ unsigned int __iomem *data1;
+ unsigned int __iomem *pmem;
+ unsigned int __iomem *reg;
+ } name;
+ unsigned int __iomem *idx[4];
+} ba1;
+
+static void cs461x_poke(unsigned long reg, unsigned int val)
+{
+ writel(val, &ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]);
+}
+
+static unsigned int cs461x_peek(unsigned long reg)
+{
+ return readl(&ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]);
+}
+
+#endif
+
+static void cs461x_pokeBA0(unsigned long reg, unsigned int val)
+{
+ writel(val, &ba0[reg >> 2]);
+}
+
+static unsigned int cs461x_peekBA0(unsigned long reg)
+{
+ return readl(&ba0[reg >> 2]);
+}
+
+static int cs461x_free(struct pci_dev *pdev)
+{
+ struct gameport *port = pci_get_drvdata(pdev);
+
+ if (port)
+ gameport_unregister_port(port);
+
+ if (ba0) iounmap(ba0);
+#ifdef CS461X_FULL_MAP
+ if (ba1.name.data0) iounmap(ba1.name.data0);
+ if (ba1.name.data1) iounmap(ba1.name.data1);
+ if (ba1.name.pmem) iounmap(ba1.name.pmem);
+ if (ba1.name.reg) iounmap(ba1.name.reg);
+#endif
+ return 0;
+}
+
+static void cs461x_gameport_trigger(struct gameport *gameport)
+{
+ cs461x_pokeBA0(BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF);
+}
+
+static unsigned char cs461x_gameport_read(struct gameport *gameport)
+{
+ return cs461x_peekBA0(BA0_JSPT); //inb(gameport->io);
+}
+
+static int cs461x_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ unsigned js1, js2, jst;
+
+ js1 = cs461x_peekBA0(BA0_JSC1);
+ js2 = cs461x_peekBA0(BA0_JSC2);
+ jst = cs461x_peekBA0(BA0_JSPT);
+
+ *buttons = (~jst >> 4) & 0x0F;
+
+ axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
+ axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
+ axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
+ axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
+
+ for(jst=0;jst<4;++jst)
+ if(axes[jst]==0xFFFF) axes[jst] = -1;
+ return 0;
+}
+
+static int cs461x_gameport_open(struct gameport *gameport, int mode)
+{
+ switch (mode) {
+ case GAMEPORT_MODE_COOKED:
+ case GAMEPORT_MODE_RAW:
+ return 0;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static struct pci_device_id cs461x_pci_tbl[] = {
+ { PCI_VENDOR_ID_CIRRUS, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4610 */
+ { PCI_VENDOR_ID_CIRRUS, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4612 */
+ { PCI_VENDOR_ID_CIRRUS, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4615 */
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, cs461x_pci_tbl);
+
+static int __devinit cs461x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int rc;
+ struct gameport* port;
+
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ printk(KERN_ERR "cs461x: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n",
+ pdev->bus->number, pdev->devfn, rc);
+ return rc;
+ }
+
+ ba0_addr = pci_resource_start(pdev, 0);
+#ifdef CS461X_FULL_MAP
+ ba1_addr = pci_resource_start(pdev, 1);
+#endif
+ if (ba0_addr == 0 || ba0_addr == ~0
+#ifdef CS461X_FULL_MAP
+ || ba1_addr == 0 || ba1_addr == ~0
+#endif
+ ) {
+ printk(KERN_ERR "cs461x: wrong address - ba0 = 0x%lx\n", ba0_addr);
+#ifdef CS461X_FULL_MAP
+ printk(KERN_ERR "cs461x: wrong address - ba1 = 0x%lx\n", ba1_addr);
+#endif
+ cs461x_free(pdev);
+ return -ENOMEM;
+ }
+
+ ba0 = ioremap(ba0_addr, CS461X_BA0_SIZE);
+#ifdef CS461X_FULL_MAP
+ ba1.name.data0 = ioremap(ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE);
+ ba1.name.data1 = ioremap(ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE);
+ ba1.name.pmem = ioremap(ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE);
+ ba1.name.reg = ioremap(ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE);
+
+ if (ba0 == NULL || ba1.name.data0 == NULL ||
+ ba1.name.data1 == NULL || ba1.name.pmem == NULL ||
+ ba1.name.reg == NULL) {
+ cs461x_free(pdev);
+ return -ENOMEM;
+ }
+#else
+ if (ba0 == NULL) {
+ cs461x_free(pdev);
+ return -ENOMEM;
+ }
+#endif
+
+ if (!(port = gameport_allocate_port())) {
+ printk(KERN_ERR "cs461x: Memory allocation failed\n");
+ cs461x_free(pdev);
+ return -ENOMEM;
+ }
+
+ pci_set_drvdata(pdev, port);
+
+ port->open = cs461x_gameport_open;
+ port->trigger = cs461x_gameport_trigger;
+ port->read = cs461x_gameport_read;
+ port->cooked_read = cs461x_gameport_cooked_read;
+
+ gameport_set_name(port, "CS416x");
+ gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
+ port->dev.parent = &pdev->dev;
+
+ cs461x_pokeBA0(BA0_JSIO, 0xFF); // ?
+ cs461x_pokeBA0(BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
+
+ gameport_register_port(port);
+
+ return 0;
+}
+
+static void __devexit cs461x_pci_remove(struct pci_dev *pdev)
+{
+ cs461x_free(pdev);
+}
+
+static struct pci_driver cs461x_pci_driver = {
+ .name = "CS461x_gameport",
+ .id_table = cs461x_pci_tbl,
+ .probe = cs461x_pci_probe,
+ .remove = __devexit_p(cs461x_pci_remove),
+};
+
+static int __init cs461x_init(void)
+{
+ return pci_register_driver(&cs461x_pci_driver);
+}
+
+static void __exit cs461x_exit(void)
+{
+ pci_unregister_driver(&cs461x_pci_driver);
+}
+
+module_init(cs461x_init);
+module_exit(cs461x_exit);
+
diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c
new file mode 100644
index 000000000000..a0118038330a
--- /dev/null
+++ b/drivers/input/gameport/emu10k1-gp.c
@@ -0,0 +1,132 @@
+/*
+ * $Id: emu10k1-gp.c,v 1.8 2002/01/22 20:40:46 vojtech Exp $
+ *
+ * Copyright (c) 2001 Vojtech Pavlik
+ */
+
+/*
+ * EMU10k1 - SB Live / Audigy - 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/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("EMU10k1 gameport driver");
+MODULE_LICENSE("GPL");
+
+struct emu {
+ struct pci_dev *dev;
+ struct gameport *gameport;
+ int io;
+ int size;
+};
+
+static struct pci_device_id emu_tbl[] = {
+
+ { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */
+ { 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */
+ { 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */
+ { 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, emu_tbl);
+
+static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int ioport, iolen;
+ struct emu *emu;
+ struct gameport *port;
+
+ if (pci_enable_device(pdev))
+ return -EBUSY;
+
+ ioport = pci_resource_start(pdev, 0);
+ iolen = pci_resource_len(pdev, 0);
+
+ if (!request_region(ioport, iolen, "emu10k1-gp"))
+ return -EBUSY;
+
+ emu = kcalloc(1, sizeof(struct emu), GFP_KERNEL);
+ port = gameport_allocate_port();
+ if (!emu || !port) {
+ printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n");
+ release_region(ioport, iolen);
+ kfree(emu);
+ gameport_free_port(port);
+ return -ENOMEM;
+ }
+
+ emu->io = ioport;
+ emu->size = iolen;
+ emu->dev = pdev;
+ emu->gameport = port;
+
+ gameport_set_name(port, "EMU10K1");
+ gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
+ port->dev.parent = &pdev->dev;
+ port->io = ioport;
+
+ pci_set_drvdata(pdev, emu);
+
+ gameport_register_port(port);
+
+ return 0;
+}
+
+static void __devexit emu_remove(struct pci_dev *pdev)
+{
+ struct emu *emu = pci_get_drvdata(pdev);
+
+ gameport_unregister_port(emu->gameport);
+ release_region(emu->io, emu->size);
+ kfree(emu);
+}
+
+static struct pci_driver emu_driver = {
+ .name = "Emu10k1_gameport",
+ .id_table = emu_tbl,
+ .probe = emu_probe,
+ .remove = __devexit_p(emu_remove),
+};
+
+static int __init emu_init(void)
+{
+ return pci_register_driver(&emu_driver);
+}
+
+static void __exit emu_exit(void)
+{
+ pci_unregister_driver(&emu_driver);
+}
+
+module_init(emu_init);
+module_exit(emu_exit);
diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c
new file mode 100644
index 000000000000..57615bc63906
--- /dev/null
+++ b/drivers/input/gameport/fm801-gp.c
@@ -0,0 +1,163 @@
+/*
+ * 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;
+};
+
+#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;
+ struct gameport *port;
+
+ gp = kcalloc(1, sizeof(struct fm801_gp), GFP_KERNEL);
+ port = gameport_allocate_port();
+ if (!gp || !port) {
+ printk(KERN_ERR "fm801-gp: Memory allocation failed\n");
+ kfree(gp);
+ gameport_free_port(port);
+ return -ENOMEM;
+ }
+
+ pci_enable_device(pci);
+
+ port->open = fm801_gp_open;
+#ifdef HAVE_COOKED
+ port->cooked_read = fm801_gp_cooked_read;
+#endif
+ gameport_set_name(port, "FM801");
+ gameport_set_phys(port, "pci%s/gameport0", pci_name(pci));
+ port->dev.parent = &pci->dev;
+ port->io = pci_resource_start(pci, 0);
+
+ gp->gameport = port;
+ gp->res_port = request_region(port->io, 0x10, "FM801 GP");
+ if (!gp->res_port) {
+ kfree(gp);
+ gameport_free_port(port);
+ printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n",
+ port->io, port->io + 0x0f);
+ return -EBUSY;
+ }
+
+ pci_set_drvdata(pci, gp);
+
+ outb(0x60, port->io + 0x0d); /* enable joystick 1 and 2 */
+ gameport_register_port(port);
+
+ 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[] = {
+ { 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_gameport",
+ .id_table = fm801_gp_id_table,
+ .probe = fm801_gp_probe,
+ .remove = __devexit_p(fm801_gp_remove),
+};
+
+static int __init fm801_gp_init(void)
+{
+ return pci_register_driver(&fm801_gp_driver);
+}
+
+static 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
new file mode 100644
index 000000000000..f20c3f23388b
--- /dev/null
+++ b/drivers/input/gameport/gameport.c
@@ -0,0 +1,797 @@
+/*
+ * Generic gameport layer
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2005 Dmitry Torokhov
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+/*#include <asm/io.h>*/
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Generic gameport layer");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(__gameport_register_port);
+EXPORT_SYMBOL(gameport_unregister_port);
+EXPORT_SYMBOL(__gameport_register_driver);
+EXPORT_SYMBOL(gameport_unregister_driver);
+EXPORT_SYMBOL(gameport_open);
+EXPORT_SYMBOL(gameport_close);
+EXPORT_SYMBOL(gameport_rescan);
+EXPORT_SYMBOL(gameport_cooked_read);
+EXPORT_SYMBOL(gameport_set_name);
+EXPORT_SYMBOL(gameport_set_phys);
+EXPORT_SYMBOL(gameport_start_polling);
+EXPORT_SYMBOL(gameport_stop_polling);
+
+/*
+ * gameport_sem protects entire gameport subsystem and is taken
+ * every time gameport port or driver registrered or unregistered.
+ */
+static DECLARE_MUTEX(gameport_sem);
+
+static LIST_HEAD(gameport_list);
+
+static struct bus_type gameport_bus = {
+ .name = "gameport",
+};
+
+static void gameport_add_port(struct gameport *gameport);
+static void gameport_destroy_port(struct gameport *gameport);
+static void gameport_reconnect_port(struct gameport *gameport);
+static void gameport_disconnect_port(struct gameport *gameport);
+
+#if defined(__i386__)
+
+#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/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__)
+
+ unsigned int i, t, t1, t2, t3, tx;
+ unsigned long flags;
+
+ if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+ return 0;
+
+ tx = 1 << 30;
+
+ for(i = 0; i < 50; i++) {
+ local_irq_save(flags);
+ GET_TIME(t1);
+ for (t = 0; t < 50; t++) gameport_read(gameport);
+ GET_TIME(t2);
+ GET_TIME(t3);
+ local_irq_restore(flags);
+ udelay(i * 10);
+ if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t;
+ }
+
+ gameport_close(gameport);
+ return 59659 / (tx < 1 ? 1 : tx);
+
+#elif defined (__x86_64__)
+
+ unsigned int i, t;
+ unsigned long tx, t1, t2, flags;
+
+ if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+ return 0;
+
+ tx = 1 << 30;
+
+ for(i = 0; i < 50; i++) {
+ local_irq_save(flags);
+ rdtscl(t1);
+ for (t = 0; t < 50; t++) gameport_read(gameport);
+ rdtscl(t2);
+ local_irq_restore(flags);
+ udelay(i * 10);
+ if (t2 - t1 < tx) tx = t2 - t1;
+ }
+
+ gameport_close(gameport);
+ return (cpu_data[_smp_processor_id()].loops_per_jiffy * (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx);
+
+#else
+
+ unsigned int j, t = 0;
+
+ if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+ return 0;
+
+ j = jiffies; while (j == jiffies);
+ j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); }
+
+ gameport_close(gameport);
+ return t * HZ / 1000;
+
+#endif
+}
+
+void gameport_start_polling(struct gameport *gameport)
+{
+ spin_lock(&gameport->timer_lock);
+
+ if (!gameport->poll_cnt++) {
+ BUG_ON(!gameport->poll_handler);
+ BUG_ON(!gameport->poll_interval);
+ mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));
+ }
+
+ spin_unlock(&gameport->timer_lock);
+}
+
+void gameport_stop_polling(struct gameport *gameport)
+{
+ spin_lock(&gameport->timer_lock);
+
+ if (!--gameport->poll_cnt)
+ del_timer(&gameport->poll_timer);
+
+ spin_unlock(&gameport->timer_lock);
+}
+
+static void gameport_run_poll_handler(unsigned long d)
+{
+ struct gameport *gameport = (struct gameport *)d;
+
+ gameport->poll_handler(gameport);
+ if (gameport->poll_cnt)
+ mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));
+}
+
+/*
+ * Basic gameport -> driver core mappings
+ */
+
+static void gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
+{
+ down_write(&gameport_bus.subsys.rwsem);
+
+ gameport->dev.driver = &drv->driver;
+ if (drv->connect(gameport, drv)) {
+ gameport->dev.driver = NULL;
+ goto out;
+ }
+ device_bind_driver(&gameport->dev);
+out:
+ up_write(&gameport_bus.subsys.rwsem);
+}
+
+static void gameport_release_driver(struct gameport *gameport)
+{
+ down_write(&gameport_bus.subsys.rwsem);
+ device_release_driver(&gameport->dev);
+ up_write(&gameport_bus.subsys.rwsem);
+}
+
+static void gameport_find_driver(struct gameport *gameport)
+{
+ down_write(&gameport_bus.subsys.rwsem);
+ device_attach(&gameport->dev);
+ up_write(&gameport_bus.subsys.rwsem);
+}
+
+
+/*
+ * Gameport event processing.
+ */
+
+enum gameport_event_type {
+ GAMEPORT_RESCAN,
+ GAMEPORT_RECONNECT,
+ GAMEPORT_REGISTER_PORT,
+ GAMEPORT_REGISTER_DRIVER,
+};
+
+struct gameport_event {
+ enum gameport_event_type type;
+ void *object;
+ struct module *owner;
+ struct list_head node;
+};
+
+static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */
+static LIST_HEAD(gameport_event_list);
+static DECLARE_WAIT_QUEUE_HEAD(gameport_wait);
+static DECLARE_COMPLETION(gameport_exited);
+static int gameport_pid;
+
+static void gameport_queue_event(void *object, struct module *owner,
+ enum gameport_event_type event_type)
+{
+ unsigned long flags;
+ struct gameport_event *event;
+
+ spin_lock_irqsave(&gameport_event_lock, flags);
+
+ /*
+ * Scan event list for the other events for the same gameport port,
+ * starting with the most recent one. If event is the same we
+ * do not need add new one. If event is of different type we
+ * need to add this event and should not look further because
+ * we need to preseve sequence of distinct events.
+ */
+ list_for_each_entry_reverse(event, &gameport_event_list, node) {
+ if (event->object == object) {
+ if (event->type == event_type)
+ goto out;
+ break;
+ }
+ }
+
+ if ((event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC))) {
+ if (!try_module_get(owner)) {
+ printk(KERN_WARNING "gameport: Can't get module reference, dropping event %d\n", event_type);
+ goto out;
+ }
+
+ event->type = event_type;
+ event->object = object;
+ event->owner = owner;
+
+ list_add_tail(&event->node, &gameport_event_list);
+ wake_up(&gameport_wait);
+ } else {
+ printk(KERN_ERR "gameport: Not enough memory to queue event %d\n", event_type);
+ }
+out:
+ spin_unlock_irqrestore(&gameport_event_lock, flags);
+}
+
+static void gameport_free_event(struct gameport_event *event)
+{
+ module_put(event->owner);
+ kfree(event);
+}
+
+static void gameport_remove_duplicate_events(struct gameport_event *event)
+{
+ struct list_head *node, *next;
+ struct gameport_event *e;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gameport_event_lock, flags);
+
+ list_for_each_safe(node, next, &gameport_event_list) {
+ e = list_entry(node, struct gameport_event, node);
+ if (event->object == e->object) {
+ /*
+ * If this event is of different type we should not
+ * look further - we only suppress duplicate events
+ * that were sent back-to-back.
+ */
+ if (event->type != e->type)
+ break;
+
+ list_del_init(node);
+ gameport_free_event(e);
+ }
+ }
+
+ spin_unlock_irqrestore(&gameport_event_lock, flags);
+}
+
+
+static struct gameport_event *gameport_get_event(void)
+{
+ struct gameport_event *event;
+ struct list_head *node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gameport_event_lock, flags);
+
+ if (list_empty(&gameport_event_list)) {
+ spin_unlock_irqrestore(&gameport_event_lock, flags);
+ return NULL;
+ }
+
+ node = gameport_event_list.next;
+ event = list_entry(node, struct gameport_event, node);
+ list_del_init(node);
+
+ spin_unlock_irqrestore(&gameport_event_lock, flags);
+
+ return event;
+}
+
+static void gameport_handle_events(void)
+{
+ struct gameport_event *event;
+ struct gameport_driver *gameport_drv;
+
+ down(&gameport_sem);
+
+ while ((event = gameport_get_event())) {
+
+ switch (event->type) {
+ case GAMEPORT_REGISTER_PORT:
+ gameport_add_port(event->object);
+ break;
+
+ case GAMEPORT_RECONNECT:
+ gameport_reconnect_port(event->object);
+ break;
+
+ case GAMEPORT_RESCAN:
+ gameport_disconnect_port(event->object);
+ gameport_find_driver(event->object);
+ break;
+
+ case GAMEPORT_REGISTER_DRIVER:
+ gameport_drv = event->object;
+ driver_register(&gameport_drv->driver);
+ break;
+
+ default:
+ break;
+ }
+
+ gameport_remove_duplicate_events(event);
+ gameport_free_event(event);
+ }
+
+ up(&gameport_sem);
+}
+
+/*
+ * Remove all events that have been submitted for a given gameport port.
+ */
+static void gameport_remove_pending_events(struct gameport *gameport)
+{
+ struct list_head *node, *next;
+ struct gameport_event *event;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gameport_event_lock, flags);
+
+ list_for_each_safe(node, next, &gameport_event_list) {
+ event = list_entry(node, struct gameport_event, node);
+ if (event->object == gameport) {
+ list_del_init(node);
+ gameport_free_event(event);
+ }
+ }
+
+ spin_unlock_irqrestore(&gameport_event_lock, flags);
+}
+
+/*
+ * Destroy child gameport port (if any) that has not been fully registered yet.
+ *
+ * Note that we rely on the fact that port can have only one child and therefore
+ * only one child registration request can be pending. Additionally, children
+ * are registered by driver's connect() handler so there can't be a grandchild
+ * pending registration together with a child.
+ */
+static struct gameport *gameport_get_pending_child(struct gameport *parent)
+{
+ struct gameport_event *event;
+ struct gameport *gameport, *child = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gameport_event_lock, flags);
+
+ list_for_each_entry(event, &gameport_event_list, node) {
+ if (event->type == GAMEPORT_REGISTER_PORT) {
+ gameport = event->object;
+ if (gameport->parent == parent) {
+ child = gameport;
+ break;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&gameport_event_lock, flags);
+ return child;
+}
+
+static int gameport_thread(void *nothing)
+{
+ lock_kernel();
+ daemonize("kgameportd");
+ allow_signal(SIGTERM);
+
+ do {
+ gameport_handle_events();
+ wait_event_interruptible(gameport_wait, !list_empty(&gameport_event_list));
+ try_to_freeze(PF_FREEZE);
+ } while (!signal_pending(current));
+
+ printk(KERN_DEBUG "gameport: kgameportd exiting\n");
+
+ unlock_kernel();
+ complete_and_exit(&gameport_exited, 0);
+}
+
+
+/*
+ * Gameport port operations
+ */
+
+static ssize_t gameport_show_description(struct device *dev, char *buf)
+{
+ struct gameport *gameport = to_gameport_port(dev);
+ return sprintf(buf, "%s\n", gameport->name);
+}
+
+static ssize_t gameport_rebind_driver(struct device *dev, const char *buf, size_t count)
+{
+ struct gameport *gameport = to_gameport_port(dev);
+ struct device_driver *drv;
+ int retval;
+
+ retval = down_interruptible(&gameport_sem);
+ if (retval)
+ return retval;
+
+ retval = count;
+ if (!strncmp(buf, "none", count)) {
+ gameport_disconnect_port(gameport);
+ } else if (!strncmp(buf, "reconnect", count)) {
+ gameport_reconnect_port(gameport);
+ } else if (!strncmp(buf, "rescan", count)) {
+ gameport_disconnect_port(gameport);
+ gameport_find_driver(gameport);
+ } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
+ gameport_disconnect_port(gameport);
+ gameport_bind_driver(gameport, to_gameport_driver(drv));
+ put_driver(drv);
+ } else {
+ retval = -EINVAL;
+ }
+
+ up(&gameport_sem);
+
+ return retval;
+}
+
+static struct device_attribute gameport_device_attrs[] = {
+ __ATTR(description, S_IRUGO, gameport_show_description, NULL),
+ __ATTR(drvctl, S_IWUSR, NULL, gameport_rebind_driver),
+ __ATTR_NULL
+};
+
+static void gameport_release_port(struct device *dev)
+{
+ struct gameport *gameport = to_gameport_port(dev);
+
+ kfree(gameport);
+ module_put(THIS_MODULE);
+}
+
+void gameport_set_phys(struct gameport *gameport, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args);
+ va_end(args);
+}
+
+/*
+ * Prepare gameport port for registration.
+ */
+static void gameport_init_port(struct gameport *gameport)
+{
+ static atomic_t gameport_no = ATOMIC_INIT(0);
+
+ __module_get(THIS_MODULE);
+
+ init_MUTEX(&gameport->drv_sem);
+ device_initialize(&gameport->dev);
+ snprintf(gameport->dev.bus_id, sizeof(gameport->dev.bus_id),
+ "gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1);
+ gameport->dev.bus = &gameport_bus;
+ gameport->dev.release = gameport_release_port;
+ if (gameport->parent)
+ gameport->dev.parent = &gameport->parent->dev;
+
+ spin_lock_init(&gameport->timer_lock);
+ init_timer(&gameport->poll_timer);
+ gameport->poll_timer.function = gameport_run_poll_handler;
+ gameport->poll_timer.data = (unsigned long)gameport;
+}
+
+/*
+ * Complete gameport port registration.
+ * Driver core will attempt to find appropriate driver for the port.
+ */
+static void gameport_add_port(struct gameport *gameport)
+{
+ if (gameport->parent)
+ gameport->parent->child = gameport;
+
+ gameport->speed = gameport_measure_speed(gameport);
+
+ list_add_tail(&gameport->node, &gameport_list);
+
+ if (gameport->io)
+ printk(KERN_INFO "gameport: %s is %s, io %#x, speed %dkHz\n",
+ gameport->name, gameport->phys, gameport->io, gameport->speed);
+ else
+ printk(KERN_INFO "gameport: %s is %s, speed %dkHz\n",
+ gameport->name, gameport->phys, gameport->speed);
+
+ device_add(&gameport->dev);
+ gameport->registered = 1;
+}
+
+/*
+ * gameport_destroy_port() completes deregistration process and removes
+ * port from the system
+ */
+static void gameport_destroy_port(struct gameport *gameport)
+{
+ struct gameport *child;
+
+ child = gameport_get_pending_child(gameport);
+ if (child) {
+ gameport_remove_pending_events(child);
+ put_device(&child->dev);
+ }
+
+ if (gameport->parent) {
+ gameport->parent->child = NULL;
+ gameport->parent = NULL;
+ }
+
+ if (gameport->registered) {
+ device_del(&gameport->dev);
+ list_del_init(&gameport->node);
+ gameport->registered = 0;
+ }
+
+ gameport_remove_pending_events(gameport);
+ put_device(&gameport->dev);
+}
+
+/*
+ * Reconnect gameport port and all its children (re-initialize attached devices)
+ */
+static void gameport_reconnect_port(struct gameport *gameport)
+{
+ do {
+ if (!gameport->drv || !gameport->drv->reconnect || gameport->drv->reconnect(gameport)) {
+ gameport_disconnect_port(gameport);
+ gameport_find_driver(gameport);
+ /* Ok, old children are now gone, we are done */
+ break;
+ }
+ gameport = gameport->child;
+ } while (gameport);
+}
+
+/*
+ * gameport_disconnect_port() unbinds a port from its driver. As a side effect
+ * all child ports are unbound and destroyed.
+ */
+static void gameport_disconnect_port(struct gameport *gameport)
+{
+ struct gameport *s, *parent;
+
+ if (gameport->child) {
+ /*
+ * Children ports should be disconnected and destroyed
+ * first, staring with the leaf one, since we don't want
+ * to do recursion
+ */
+ for (s = gameport; s->child; s = s->child)
+ /* empty */;
+
+ do {
+ parent = s->parent;
+
+ gameport_release_driver(s);
+ gameport_destroy_port(s);
+ } while ((s = parent) != gameport);
+ }
+
+ /*
+ * Ok, no children left, now disconnect this port
+ */
+ gameport_release_driver(gameport);
+}
+
+void gameport_rescan(struct gameport *gameport)
+{
+ gameport_queue_event(gameport, NULL, GAMEPORT_RESCAN);
+}
+
+void gameport_reconnect(struct gameport *gameport)
+{
+ gameport_queue_event(gameport, NULL, GAMEPORT_RECONNECT);
+}
+
+/*
+ * Submits register request to kgameportd for subsequent execution.
+ * Note that port registration is always asynchronous.
+ */
+void __gameport_register_port(struct gameport *gameport, struct module *owner)
+{
+ gameport_init_port(gameport);
+ gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT);
+}
+
+/*
+ * Synchronously unregisters gameport port.
+ */
+void gameport_unregister_port(struct gameport *gameport)
+{
+ down(&gameport_sem);
+ gameport_disconnect_port(gameport);
+ gameport_destroy_port(gameport);
+ up(&gameport_sem);
+}
+
+
+/*
+ * Gameport driver operations
+ */
+
+static ssize_t gameport_driver_show_description(struct device_driver *drv, char *buf)
+{
+ struct gameport_driver *driver = to_gameport_driver(drv);
+ return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
+}
+
+static struct driver_attribute gameport_driver_attrs[] = {
+ __ATTR(description, S_IRUGO, gameport_driver_show_description, NULL),
+ __ATTR_NULL
+};
+
+static int gameport_driver_probe(struct device *dev)
+{
+ struct gameport *gameport = to_gameport_port(dev);
+ struct gameport_driver *drv = to_gameport_driver(dev->driver);
+
+ drv->connect(gameport, drv);
+ return gameport->drv ? 0 : -ENODEV;
+}
+
+static int gameport_driver_remove(struct device *dev)
+{
+ struct gameport *gameport = to_gameport_port(dev);
+ struct gameport_driver *drv = to_gameport_driver(dev->driver);
+
+ drv->disconnect(gameport);
+ return 0;
+}
+
+void __gameport_register_driver(struct gameport_driver *drv, struct module *owner)
+{
+ drv->driver.bus = &gameport_bus;
+ drv->driver.probe = gameport_driver_probe;
+ drv->driver.remove = gameport_driver_remove;
+ gameport_queue_event(drv, owner, GAMEPORT_REGISTER_DRIVER);
+}
+
+void gameport_unregister_driver(struct gameport_driver *drv)
+{
+ struct gameport *gameport;
+
+ down(&gameport_sem);
+ drv->ignore = 1; /* so gameport_find_driver ignores it */
+
+start_over:
+ list_for_each_entry(gameport, &gameport_list, node) {
+ if (gameport->drv == drv) {
+ gameport_disconnect_port(gameport);
+ gameport_find_driver(gameport);
+ /* we could've deleted some ports, restart */
+ goto start_over;
+ }
+ }
+
+ driver_unregister(&drv->driver);
+ up(&gameport_sem);
+}
+
+static int gameport_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct gameport_driver *gameport_drv = to_gameport_driver(drv);
+
+ return !gameport_drv->ignore;
+}
+
+static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv)
+{
+ down(&gameport->drv_sem);
+ gameport->drv = drv;
+ up(&gameport->drv_sem);
+}
+
+int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode)
+{
+
+ if (gameport->open) {
+ if (gameport->open(gameport, mode)) {
+ return -1;
+ }
+ } else {
+ if (mode != GAMEPORT_MODE_RAW)
+ return -1;
+ }
+
+ gameport_set_drv(gameport, drv);
+ return 0;
+}
+
+void gameport_close(struct gameport *gameport)
+{
+ del_timer_sync(&gameport->poll_timer);
+ gameport->poll_handler = NULL;
+ gameport->poll_interval = 0;
+ gameport_set_drv(gameport, NULL);
+ if (gameport->close)
+ gameport->close(gameport);
+}
+
+static int __init gameport_init(void)
+{
+ if (!(gameport_pid = kernel_thread(gameport_thread, NULL, CLONE_KERNEL))) {
+ printk(KERN_ERR "gameport: Failed to start kgameportd\n");
+ return -1;
+ }
+
+ gameport_bus.dev_attrs = gameport_device_attrs;
+ gameport_bus.drv_attrs = gameport_driver_attrs;
+ gameport_bus.match = gameport_bus_match;
+ bus_register(&gameport_bus);
+
+ return 0;
+}
+
+static void __exit gameport_exit(void)
+{
+ bus_unregister(&gameport_bus);
+ kill_proc(gameport_pid, SIGTERM, 1);
+ wait_for_completion(&gameport_exited);
+}
+
+module_init(gameport_init);
+module_exit(gameport_exit);
diff --git a/drivers/input/gameport/lightning.c b/drivers/input/gameport/lightning.c
new file mode 100644
index 000000000000..d65d81080257
--- /dev/null
+++ b/drivers/input/gameport/lightning.c
@@ -0,0 +1,344 @@
+/*
+ * $Id: lightning.c,v 1.20 2002/01/22 20:41:31 vojtech Exp $
+ *
+ * Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * PDPI Lightning 4 gamecard 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/init.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+
+#define L4_PORT 0x201
+#define L4_SELECT_ANALOG 0xa4
+#define L4_SELECT_DIGITAL 0xa5
+#define L4_SELECT_SECONDARY 0xa6
+#define L4_CMD_ID 0x80
+#define L4_CMD_GETCAL 0x92
+#define L4_CMD_SETCAL 0x93
+#define L4_ID 0x04
+#define L4_BUSY 0x01
+#define L4_TIMEOUT 80 /* 80 us */
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
+MODULE_LICENSE("GPL");
+
+struct l4 {
+ struct gameport *gameport;
+ unsigned char port;
+};
+
+static struct l4 l4_ports[8];
+
+/*
+ * l4_wait_ready() waits for the L4 to become ready.
+ */
+
+static int l4_wait_ready(void)
+{
+ unsigned int t = L4_TIMEOUT;
+
+ while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
+ return -(t <= 0);
+}
+
+/*
+ * l4_cooked_read() reads data from the Lightning 4.
+ */
+
+static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ struct l4 *l4 = gameport->port_data;
+ unsigned char status;
+ int i, result = -1;
+
+ outb(L4_SELECT_ANALOG, L4_PORT);
+ outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
+
+ if (inb(L4_PORT) & L4_BUSY) goto fail;
+ outb(l4->port & 3, L4_PORT);
+
+ if (l4_wait_ready()) goto fail;
+ status = inb(L4_PORT);
+
+ for (i = 0; i < 4; i++)
+ if (status & (1 << i)) {
+ if (l4_wait_ready()) goto fail;
+ axes[i] = inb(L4_PORT);
+ if (axes[i] > 252) axes[i] = -1;
+ }
+
+ if (status & 0x10) {
+ if (l4_wait_ready()) goto fail;
+ *buttons = inb(L4_PORT) & 0x0f;
+ }
+
+ result = 0;
+
+fail: outb(L4_SELECT_ANALOG, L4_PORT);
+ return result;
+}
+
+static int l4_open(struct gameport *gameport, int mode)
+{
+ struct l4 *l4 = gameport->port_data;
+
+ if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
+ return -1;
+ outb(L4_SELECT_ANALOG, L4_PORT);
+ return 0;
+}
+
+/*
+ * l4_getcal() reads the L4 with calibration values.
+ */
+
+static int l4_getcal(int port, int *cal)
+{
+ int i, result = -1;
+
+ outb(L4_SELECT_ANALOG, L4_PORT);
+ outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
+ if (inb(L4_PORT) & L4_BUSY)
+ goto out;
+
+ outb(L4_CMD_GETCAL, L4_PORT);
+ if (l4_wait_ready())
+ goto out;
+
+ if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
+ goto out;
+
+ if (l4_wait_ready())
+ goto out;
+ outb(port & 3, L4_PORT);
+
+ for (i = 0; i < 4; i++) {
+ if (l4_wait_ready())
+ goto out;
+ cal[i] = inb(L4_PORT);
+ }
+
+ result = 0;
+
+out: outb(L4_SELECT_ANALOG, L4_PORT);
+ return result;
+}
+
+/*
+ * l4_setcal() programs the L4 with calibration values.
+ */
+
+static int l4_setcal(int port, int *cal)
+{
+ int i, result = -1;
+
+ outb(L4_SELECT_ANALOG, L4_PORT);
+ outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
+ if (inb(L4_PORT) & L4_BUSY)
+ goto out;
+
+ outb(L4_CMD_SETCAL, L4_PORT);
+ if (l4_wait_ready())
+ goto out;
+
+ if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
+ goto out;
+
+ if (l4_wait_ready())
+ goto out;
+ outb(port & 3, L4_PORT);
+
+ for (i = 0; i < 4; i++) {
+ if (l4_wait_ready())
+ goto out;
+ outb(cal[i], L4_PORT);
+ }
+
+ result = 0;
+
+out: outb(L4_SELECT_ANALOG, L4_PORT);
+ return result;
+}
+
+/*
+ * l4_calibrate() calibrates the L4 for the attached device, so
+ * that the device's resistance fits into the L4's 8-bit range.
+ */
+
+static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
+{
+ int i, t;
+ int cal[4];
+ struct l4 *l4 = gameport->port_data;
+
+ if (l4_getcal(l4->port, cal))
+ return -1;
+
+ for (i = 0; i < 4; i++) {
+ t = (max[i] * cal[i]) / 200;
+ t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
+ axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
+ axes[i] = (axes[i] > 252) ? 252 : axes[i];
+ cal[i] = t;
+ }
+
+ if (l4_setcal(l4->port, cal))
+ return -1;
+
+ return 0;
+}
+
+static int __init l4_create_ports(int card_no)
+{
+ struct l4 *l4;
+ struct gameport *port;
+ int i, idx;
+
+ for (i = 0; i < 4; i++) {
+
+ idx = card_no * 4 + i;
+ l4 = &l4_ports[idx];
+
+ if (!(l4->gameport = port = gameport_allocate_port())) {
+ printk(KERN_ERR "lightning: Memory allocation failed\n");
+ while (--i >= 0) {
+ gameport_free_port(l4->gameport);
+ l4->gameport = NULL;
+ }
+ return -ENOMEM;
+ }
+ l4->port = idx;
+
+ port->port_data = l4;
+ port->open = l4_open;
+ port->cooked_read = l4_cooked_read;
+ port->calibrate = l4_calibrate;
+
+ gameport_set_name(port, "PDPI Lightning 4");
+ gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx);
+
+ if (idx == 0)
+ port->io = L4_PORT;
+ }
+
+ return 0;
+}
+
+static int __init l4_add_card(int card_no)
+{
+ int cal[4] = { 255, 255, 255, 255 };
+ int i, rev, result;
+ struct l4 *l4;
+
+ outb(L4_SELECT_ANALOG, L4_PORT);
+ outb(L4_SELECT_DIGITAL + card_no, L4_PORT);
+
+ if (inb(L4_PORT) & L4_BUSY)
+ return -1;
+ outb(L4_CMD_ID, L4_PORT);
+
+ if (l4_wait_ready())
+ return -1;
+
+ if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)
+ return -1;
+
+ if (l4_wait_ready())
+ return -1;
+ if (inb(L4_PORT) != L4_ID)
+ return -1;
+
+ if (l4_wait_ready())
+ return -1;
+ rev = inb(L4_PORT);
+
+ if (!rev)
+ return -1;
+
+ result = l4_create_ports(card_no);
+ if (result)
+ return result;
+
+ printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
+ card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
+
+ for (i = 0; i < 4; i++) {
+ l4 = &l4_ports[card_no * 4 + i];
+
+ if (rev > 0x28) /* on 2.9+ the setcal command works correctly */
+ l4_setcal(l4->port, cal);
+ gameport_register_port(l4->gameport);
+ }
+
+ return 0;
+}
+
+static int __init l4_init(void)
+{
+ int i, cards = 0;
+
+ if (!request_region(L4_PORT, 1, "lightning"))
+ return -1;
+
+ for (i = 0; i < 2; i++)
+ if (l4_add_card(i) == 0)
+ cards++;
+
+ outb(L4_SELECT_ANALOG, L4_PORT);
+
+ if (!cards) {
+ release_region(L4_PORT, 1);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __exit l4_exit(void)
+{
+ int i;
+ int cal[4] = { 59, 59, 59, 59 };
+
+ for (i = 0; i < 8; i++)
+ if (l4_ports[i].gameport) {
+ l4_setcal(l4_ports[i].port, cal);
+ gameport_unregister_port(l4_ports[i].gameport);
+ }
+
+ outb(L4_SELECT_ANALOG, L4_PORT);
+ release_region(L4_PORT, 1);
+}
+
+module_init(l4_init);
+module_exit(l4_exit);
diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c
new file mode 100644
index 000000000000..7c5c6318eeb9
--- /dev/null
+++ b/drivers/input/gameport/ns558.c
@@ -0,0 +1,291 @@
+/*
+ * $Id: ns558.c,v 1.43 2002/01/24 19:23:21 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ * Copyright (c) 1999 Brian Gerst
+ */
+
+/*
+ * NS558 based standard IBM game 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
+ *
+ * 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/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/pnp.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver");
+MODULE_LICENSE("GPL");
+
+static int ns558_isa_portlist[] = { 0x201, 0x200, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209,
+ 0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 };
+
+struct ns558 {
+ int type;
+ int io;
+ int size;
+ struct pnp_dev *dev;
+ struct gameport *gameport;
+ struct list_head node;
+};
+
+static LIST_HEAD(ns558_list);
+
+/*
+ * ns558_isa_probe() tries to find an isa gameport at the
+ * specified address, and also checks for mirrors.
+ * A joystick must be attached for this to work.
+ */
+
+static int ns558_isa_probe(int io)
+{
+ int i, j, b;
+ unsigned char c, u, v;
+ struct ns558 *ns558;
+ struct gameport *port;
+
+/*
+ * No one should be using this address.
+ */
+
+ if (!request_region(io, 1, "ns558-isa"))
+ return -EBUSY;
+
+/*
+ * We must not be able to write arbitrary values to the port.
+ * The lower two axis bits must be 1 after a write.
+ */
+
+ c = inb(io);
+ outb(~c & ~3, io);
+ if (~(u = v = inb(io)) & 3) {
+ outb(c, io);
+ release_region(io, 1);
+ return -ENODEV;
+ }
+/*
+ * After a trigger, there must be at least some bits changing.
+ */
+
+ for (i = 0; i < 1000; i++) v &= inb(io);
+
+ if (u == v) {
+ outb(c, io);
+ release_region(io, 1);
+ return -ENODEV;
+ }
+ msleep(3);
+/*
+ * After some time (4ms) the axes shouldn't change anymore.
+ */
+
+ u = inb(io);
+ for (i = 0; i < 1000; i++)
+ if ((u ^ inb(io)) & 0xf) {
+ outb(c, io);
+ release_region(io, 1);
+ return -ENODEV;
+ }
+/*
+ * And now find the number of mirrors of the port.
+ */
+
+ for (i = 1; i < 5; i++) {
+
+ release_region(io & (-1 << (i - 1)), (1 << (i - 1)));
+
+ if (!request_region(io & (-1 << i), (1 << i), "ns558-isa"))
+ break; /* Don't disturb anyone */
+
+ outb(0xff, io & (-1 << i));
+ for (j = b = 0; j < 1000; j++)
+ if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++;
+ msleep(3);
+
+ if (b > 300) { /* We allow 30% difference */
+ release_region(io & (-1 << i), (1 << i));
+ break;
+ }
+ }
+
+ i--;
+
+ if (i != 4) {
+ if (!request_region(io & (-1 << i), (1 << i), "ns558-isa"))
+ return -EBUSY;
+ }
+
+ ns558 = kcalloc(1, sizeof(struct ns558), GFP_KERNEL);
+ port = gameport_allocate_port();
+ if (!ns558 || !port) {
+ printk(KERN_ERR "ns558: Memory allocation failed.\n");
+ release_region(io & (-1 << i), (1 << i));
+ kfree(ns558);
+ gameport_free_port(port);
+ return -ENOMEM;
+ }
+
+ memset(ns558, 0, sizeof(struct ns558));
+ ns558->io = io;
+ ns558->size = 1 << i;
+ ns558->gameport = port;
+
+ port->io = io;
+ gameport_set_name(port, "NS558 ISA Gameport");
+ gameport_set_phys(port, "isa%04x/gameport0", io & (-1 << i));
+
+ gameport_register_port(port);
+
+ list_add(&ns558->node, &ns558_list);
+
+ return 0;
+}
+
+#ifdef CONFIG_PNP
+
+static struct pnp_device_id pnp_devids[] = {
+ { .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */
+ { .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */
+ { .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */
+ { .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */
+ { .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */
+ { .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */
+ { .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */
+ { .id = "CSC0001", .driver_data = 0 }, /* CS4232 */
+ { .id = "CSC000f", .driver_data = 0 }, /* CS4236 */
+ { .id = "CSC0101", .driver_data = 0 }, /* CS4327 */
+ { .id = "CTL7001", .driver_data = 0 }, /* SB16 */
+ { .id = "CTL7002", .driver_data = 0 }, /* AWE64 */
+ { .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */
+ { .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */
+ { .id = "ESS0001", .driver_data = 0 }, /* ES1869 */
+ { .id = "ESS0005", .driver_data = 0 }, /* ES1878 */
+ { .id = "ESS6880", .driver_data = 0 }, /* ES688 */
+ { .id = "IBM0012", .driver_data = 0 }, /* CS4232 */
+ { .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */
+ { .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */
+ { .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */
+ { .id = "PNPb02f", .driver_data = 0 }, /* Generic */
+ { .id = "", },
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_devids);
+
+static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
+{
+ int ioport, iolen;
+ struct ns558 *ns558;
+ struct gameport *port;
+
+ if (!pnp_port_valid(dev, 0)) {
+ printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n");
+ return -ENODEV;
+ }
+
+ ioport = pnp_port_start(dev, 0);
+ iolen = pnp_port_len(dev, 0);
+
+ if (!request_region(ioport, iolen, "ns558-pnp"))
+ return -EBUSY;
+
+ ns558 = kcalloc(1, sizeof(struct ns558), GFP_KERNEL);
+ port = gameport_allocate_port();
+ if (!ns558 || !port) {
+ printk(KERN_ERR "ns558: Memory allocation failed\n");
+ kfree(ns558);
+ gameport_free_port(port);
+ return -ENOMEM;
+ }
+
+ ns558->io = ioport;
+ ns558->size = iolen;
+ ns558->dev = dev;
+ ns558->gameport = port;
+
+ gameport_set_name(port, "NS558 PnP Gameport");
+ gameport_set_phys(port, "pnp%s/gameport0", dev->dev.bus_id);
+ port->dev.parent = &dev->dev;
+ port->io = ioport;
+
+ gameport_register_port(port);
+
+ list_add_tail(&ns558->node, &ns558_list);
+ return 0;
+}
+
+static struct pnp_driver ns558_pnp_driver = {
+ .name = "ns558",
+ .id_table = pnp_devids,
+ .probe = ns558_pnp_probe,
+};
+
+#else
+
+static struct pnp_driver ns558_pnp_driver;
+
+#endif
+
+static int pnp_registered = 0;
+
+static int __init ns558_init(void)
+{
+ int i = 0;
+
+/*
+ * Probe ISA ports first so that PnP gets to choose free port addresses
+ * not occupied by the ISA ports.
+ */
+
+ while (ns558_isa_portlist[i])
+ ns558_isa_probe(ns558_isa_portlist[i++]);
+
+ if (pnp_register_driver(&ns558_pnp_driver) >= 0)
+ pnp_registered = 1;
+
+
+ return (list_empty(&ns558_list) && !pnp_registered) ? -ENODEV : 0;
+}
+
+static void __exit ns558_exit(void)
+{
+ struct ns558 *ns558;
+
+ list_for_each_entry(ns558, &ns558_list, node) {
+ gameport_unregister_port(ns558->gameport);
+ release_region(ns558->io & ~(ns558->size - 1), ns558->size);
+ kfree(ns558);
+ }
+
+ if (pnp_registered)
+ pnp_unregister_driver(&ns558_pnp_driver);
+}
+
+module_init(ns558_init);
+module_exit(ns558_exit);
diff --git a/drivers/input/gameport/vortex.c b/drivers/input/gameport/vortex.c
new file mode 100644
index 000000000000..36b0309c8bf6
--- /dev/null
+++ b/drivers/input/gameport/vortex.c
@@ -0,0 +1,186 @@
+/*
+ * $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/delay.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 __iomem *base;
+ unsigned char __iomem *io;
+};
+
+static unsigned char vortex_read(struct gameport *gameport)
+{
+ struct vortex *vortex = gameport->port_data;
+ return readb(vortex->io + VORTEX_LEG);
+}
+
+static void vortex_trigger(struct gameport *gameport)
+{
+ struct vortex *vortex = gameport->port_data;
+ writeb(0xff, vortex->io + VORTEX_LEG);
+}
+
+static int vortex_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ struct vortex *vortex = gameport->port_data;
+ 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->port_data;
+
+ switch (mode) {
+ case GAMEPORT_MODE_COOKED:
+ writeb(0x40, vortex->io + VORTEX_GCR);
+ msleep(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;
+ struct gameport *port;
+ int i;
+
+ vortex = kcalloc(1, sizeof(struct vortex), GFP_KERNEL);
+ port = gameport_allocate_port();
+ if (!vortex || !port) {
+ printk(KERN_ERR "vortex: Memory allocation failed.\n");
+ kfree(vortex);
+ gameport_free_port(port);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < 6; i++)
+ if (~pci_resource_flags(dev, i) & IORESOURCE_IO)
+ break;
+
+ pci_enable_device(dev);
+
+ vortex->dev = dev;
+ vortex->gameport = port;
+ vortex->base = ioremap(pci_resource_start(vortex->dev, i),
+ pci_resource_len(vortex->dev, i));
+ vortex->io = vortex->base + id->driver_data;
+
+ pci_set_drvdata(dev, vortex);
+
+ port->port_data = vortex;
+ port->fuzz = 64;
+
+ gameport_set_name(port, "AU88x0");
+ gameport_set_phys(port, "pci%s/gameport0", pci_name(dev));
+ port->dev.parent = &dev->dev;
+ port->read = vortex_read;
+ port->trigger = vortex_trigger;
+ port->cooked_read = vortex_cooked_read;
+ port->open = vortex_open;
+
+ gameport_register_port(port);
+
+ 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[] = {
+ { 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_gameport",
+ .id_table = vortex_id_table,
+ .probe = vortex_probe,
+ .remove = __devexit_p(vortex_remove),
+};
+
+static int __init vortex_init(void)
+{
+ return pci_register_driver(&vortex_driver);
+}
+
+static 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
new file mode 100644
index 000000000000..3385dd03abfc
--- /dev/null
+++ b/drivers/input/input.c
@@ -0,0 +1,748 @@
+/*
+ * The input core
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/major.h>
+#include <linux/proc_fs.h>
+#include <linux/kobject_uevent.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input core");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(input_register_device);
+EXPORT_SYMBOL(input_unregister_device);
+EXPORT_SYMBOL(input_register_handler);
+EXPORT_SYMBOL(input_unregister_handler);
+EXPORT_SYMBOL(input_grab_device);
+EXPORT_SYMBOL(input_release_device);
+EXPORT_SYMBOL(input_open_device);
+EXPORT_SYMBOL(input_close_device);
+EXPORT_SYMBOL(input_accept_process);
+EXPORT_SYMBOL(input_flush_device);
+EXPORT_SYMBOL(input_event);
+EXPORT_SYMBOL(input_class);
+
+#define INPUT_DEVICES 256
+
+static LIST_HEAD(input_dev_list);
+static LIST_HEAD(input_handler_list);
+
+static struct input_handler *input_table[8];
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_bus_input_dir;
+static DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait);
+static int input_devices_state;
+#endif
+
+void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct input_handle *handle;
+
+ if (type > EV_MAX || !test_bit(type, dev->evbit))
+ return;
+
+ add_input_randomness(type, code, value);
+
+ switch (type) {
+
+ case EV_SYN:
+ switch (code) {
+ case SYN_CONFIG:
+ if (dev->event) dev->event(dev, type, code, value);
+ break;
+
+ case SYN_REPORT:
+ if (dev->sync) return;
+ dev->sync = 1;
+ break;
+ }
+ break;
+
+ case EV_KEY:
+
+ if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
+ return;
+
+ if (value == 2)
+ break;
+
+ change_bit(code, dev->key);
+
+ if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
+ dev->repeat_key = code;
+ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+ }
+
+ break;
+
+ case EV_ABS:
+
+ if (code > ABS_MAX || !test_bit(code, dev->absbit))
+ return;
+
+ if (dev->absfuzz[code]) {
+ if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
+ (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
+ return;
+
+ if ((value > dev->abs[code] - dev->absfuzz[code]) &&
+ (value < dev->abs[code] + dev->absfuzz[code]))
+ value = (dev->abs[code] * 3 + value) >> 2;
+
+ if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
+ (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
+ value = (dev->abs[code] + value) >> 1;
+ }
+
+ if (dev->abs[code] == value)
+ return;
+
+ dev->abs[code] = value;
+ break;
+
+ case EV_REL:
+
+ if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
+ return;
+
+ break;
+
+ case EV_MSC:
+
+ if (code > MSC_MAX || !test_bit(code, dev->mscbit))
+ return;
+
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_LED:
+
+ if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
+ return;
+
+ change_bit(code, dev->led);
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_SND:
+
+ if (code > SND_MAX || !test_bit(code, dev->sndbit))
+ return;
+
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_REP:
+
+ if (code > REP_MAX || value < 0 || dev->rep[code] == value) return;
+
+ dev->rep[code] = value;
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_FF:
+ if (dev->event) dev->event(dev, type, code, value);
+ break;
+ }
+
+ if (type != EV_SYN)
+ dev->sync = 0;
+
+ if (dev->grab)
+ dev->grab->handler->event(dev->grab, type, code, value);
+ else
+ list_for_each_entry(handle, &dev->h_list, d_node)
+ if (handle->open)
+ handle->handler->event(handle, type, code, value);
+}
+
+static void input_repeat_key(unsigned long data)
+{
+ struct input_dev *dev = (void *) data;
+
+ if (!test_bit(dev->repeat_key, dev->key))
+ return;
+
+ input_event(dev, EV_KEY, dev->repeat_key, 2);
+ input_sync(dev);
+
+ if (dev->rep[REP_PERIOD])
+ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
+}
+
+int input_accept_process(struct input_handle *handle, struct file *file)
+{
+ if (handle->dev->accept)
+ return handle->dev->accept(handle->dev, file);
+
+ return 0;
+}
+
+int input_grab_device(struct input_handle *handle)
+{
+ if (handle->dev->grab)
+ return -EBUSY;
+
+ handle->dev->grab = handle;
+ return 0;
+}
+
+void input_release_device(struct input_handle *handle)
+{
+ if (handle->dev->grab == handle)
+ handle->dev->grab = NULL;
+}
+
+int input_open_device(struct input_handle *handle)
+{
+ handle->open++;
+ if (handle->dev->open)
+ return handle->dev->open(handle->dev);
+ return 0;
+}
+
+int input_flush_device(struct input_handle* handle, struct file* file)
+{
+ if (handle->dev->flush)
+ return handle->dev->flush(handle->dev, file);
+
+ return 0;
+}
+
+void input_close_device(struct input_handle *handle)
+{
+ input_release_device(handle);
+ if (handle->dev->close)
+ handle->dev->close(handle->dev);
+ handle->open--;
+}
+
+static void input_link_handle(struct input_handle *handle)
+{
+ list_add_tail(&handle->d_node, &handle->dev->h_list);
+ list_add_tail(&handle->h_node, &handle->handler->h_list);
+}
+
+#define MATCH_BIT(bit, max) \
+ for (i = 0; i < NBITS(max); i++) \
+ if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
+ break; \
+ if (i != NBITS(max)) \
+ continue;
+
+static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
+{
+ int i;
+
+ for (; id->flags || id->driver_info; id++) {
+
+ if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
+ if (id->id.bustype != dev->id.bustype)
+ continue;
+
+ if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
+ if (id->id.vendor != dev->id.vendor)
+ continue;
+
+ if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
+ if (id->id.product != dev->id.product)
+ continue;
+
+ if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
+ if (id->id.version != dev->id.version)
+ continue;
+
+ MATCH_BIT(evbit, EV_MAX);
+ MATCH_BIT(keybit, KEY_MAX);
+ MATCH_BIT(relbit, REL_MAX);
+ MATCH_BIT(absbit, ABS_MAX);
+ MATCH_BIT(mscbit, MSC_MAX);
+ MATCH_BIT(ledbit, LED_MAX);
+ MATCH_BIT(sndbit, SND_MAX);
+ MATCH_BIT(ffbit, FF_MAX);
+
+ return id;
+ }
+
+ return NULL;
+}
+
+/*
+ * Input hotplugging interface - loading event handlers based on
+ * device bitfields.
+ */
+
+#ifdef CONFIG_HOTPLUG
+
+/*
+ * Input hotplugging invokes what /proc/sys/kernel/hotplug says
+ * (normally /sbin/hotplug) when input devices get added or removed.
+ *
+ * This invokes a user mode policy agent, typically helping to load driver
+ * or other modules, configure the device, and more. Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+ *
+ */
+
+#define SPRINTF_BIT_A(bit, name, max) \
+ do { \
+ envp[i++] = scratch; \
+ scratch += sprintf(scratch, name); \
+ for (j = NBITS(max) - 1; j >= 0; j--) \
+ if (dev->bit[j]) break; \
+ for (; j >= 0; j--) \
+ scratch += sprintf(scratch, "%lx ", dev->bit[j]); \
+ scratch++; \
+ } while (0)
+
+#define SPRINTF_BIT_A2(bit, name, max, ev) \
+ do { \
+ if (test_bit(ev, dev->evbit)) \
+ SPRINTF_BIT_A(bit, name, max); \
+ } while (0)
+
+static void input_call_hotplug(char *verb, struct input_dev *dev)
+{
+ char *argv[3], **envp, *buf, *scratch;
+ int i = 0, j, value;
+
+ if (!hotplug_path[0]) {
+ printk(KERN_ERR "input.c: calling hotplug without a hotplug agent defined\n");
+ return;
+ }
+ if (in_interrupt()) {
+ printk(KERN_ERR "input.c: calling hotplug from interrupt\n");
+ return;
+ }
+ if (!current->fs->root) {
+ printk(KERN_WARNING "input.c: calling hotplug without valid filesystem\n");
+ return;
+ }
+ if (!(envp = (char **) kmalloc(20 * sizeof(char *), GFP_KERNEL))) {
+ printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n");
+ return;
+ }
+ if (!(buf = kmalloc(1024, GFP_KERNEL))) {
+ kfree (envp);
+ printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n");
+ return;
+ }
+
+ argv[0] = hotplug_path;
+ argv[1] = "input";
+ argv[2] = NULL;
+
+ envp[i++] = "HOME=/";
+ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+ scratch = buf;
+
+ envp[i++] = scratch;
+ scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
+
+ envp[i++] = scratch;
+ scratch += sprintf(scratch, "PRODUCT=%x/%x/%x/%x",
+ dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version) + 1;
+
+ if (dev->name) {
+ envp[i++] = scratch;
+ scratch += sprintf(scratch, "NAME=%s", dev->name) + 1;
+ }
+
+ if (dev->phys) {
+ envp[i++] = scratch;
+ scratch += sprintf(scratch, "PHYS=%s", dev->phys) + 1;
+ }
+
+ SPRINTF_BIT_A(evbit, "EV=", EV_MAX);
+ SPRINTF_BIT_A2(keybit, "KEY=", KEY_MAX, EV_KEY);
+ SPRINTF_BIT_A2(relbit, "REL=", REL_MAX, EV_REL);
+ SPRINTF_BIT_A2(absbit, "ABS=", ABS_MAX, EV_ABS);
+ SPRINTF_BIT_A2(mscbit, "MSC=", MSC_MAX, EV_MSC);
+ SPRINTF_BIT_A2(ledbit, "LED=", LED_MAX, EV_LED);
+ SPRINTF_BIT_A2(sndbit, "SND=", SND_MAX, EV_SND);
+ SPRINTF_BIT_A2(ffbit, "FF=", FF_MAX, EV_FF);
+
+ envp[i++] = NULL;
+
+#ifdef INPUT_DEBUG
+ printk(KERN_DEBUG "input.c: calling %s %s [%s %s %s %s %s]\n",
+ argv[0], argv[1], envp[0], envp[1], envp[2], envp[3], envp[4]);
+#endif
+
+ value = call_usermodehelper(argv [0], argv, envp, 0);
+
+ kfree(buf);
+ kfree(envp);
+
+#ifdef INPUT_DEBUG
+ if (value != 0)
+ printk(KERN_DEBUG "input.c: hotplug returned %d\n", value);
+#endif
+}
+
+#endif
+
+void input_register_device(struct input_dev *dev)
+{
+ struct input_handle *handle;
+ struct input_handler *handler;
+ struct input_device_id *id;
+
+ set_bit(EV_SYN, dev->evbit);
+
+ /*
+ * If delay and period are pre-set by the driver, then autorepeating
+ * is handled by the driver itself and we don't do it in input.c.
+ */
+
+ init_timer(&dev->timer);
+ if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
+ dev->timer.data = (long) dev;
+ dev->timer.function = input_repeat_key;
+ dev->rep[REP_DELAY] = 250;
+ dev->rep[REP_PERIOD] = 33;
+ }
+
+ INIT_LIST_HEAD(&dev->h_list);
+ list_add_tail(&dev->node, &input_dev_list);
+
+ list_for_each_entry(handler, &input_handler_list, node)
+ if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
+ if ((id = input_match_device(handler->id_table, dev)))
+ if ((handle = handler->connect(handler, dev, id)))
+ input_link_handle(handle);
+
+#ifdef CONFIG_HOTPLUG
+ input_call_hotplug("add", dev);
+#endif
+
+#ifdef CONFIG_PROC_FS
+ input_devices_state++;
+ wake_up(&input_devices_poll_wait);
+#endif
+}
+
+void input_unregister_device(struct input_dev *dev)
+{
+ struct list_head * node, * next;
+
+ if (!dev) return;
+
+ del_timer_sync(&dev->timer);
+
+ list_for_each_safe(node, next, &dev->h_list) {
+ struct input_handle * handle = to_handle(node);
+ list_del_init(&handle->d_node);
+ list_del_init(&handle->h_node);
+ handle->handler->disconnect(handle);
+ }
+
+#ifdef CONFIG_HOTPLUG
+ input_call_hotplug("remove", dev);
+#endif
+
+ list_del_init(&dev->node);
+
+#ifdef CONFIG_PROC_FS
+ input_devices_state++;
+ wake_up(&input_devices_poll_wait);
+#endif
+}
+
+void input_register_handler(struct input_handler *handler)
+{
+ struct input_dev *dev;
+ struct input_handle *handle;
+ struct input_device_id *id;
+
+ if (!handler) return;
+
+ INIT_LIST_HEAD(&handler->h_list);
+
+ if (handler->fops != NULL)
+ input_table[handler->minor >> 5] = handler;
+
+ list_add_tail(&handler->node, &input_handler_list);
+
+ list_for_each_entry(dev, &input_dev_list, node)
+ if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
+ if ((id = input_match_device(handler->id_table, dev)))
+ if ((handle = handler->connect(handler, dev, id)))
+ input_link_handle(handle);
+
+#ifdef CONFIG_PROC_FS
+ input_devices_state++;
+ wake_up(&input_devices_poll_wait);
+#endif
+}
+
+void input_unregister_handler(struct input_handler *handler)
+{
+ struct list_head * node, * next;
+
+ list_for_each_safe(node, next, &handler->h_list) {
+ struct input_handle * handle = to_handle_h(node);
+ list_del_init(&handle->h_node);
+ list_del_init(&handle->d_node);
+ handler->disconnect(handle);
+ }
+
+ list_del_init(&handler->node);
+
+ if (handler->fops != NULL)
+ input_table[handler->minor >> 5] = NULL;
+
+#ifdef CONFIG_PROC_FS
+ input_devices_state++;
+ wake_up(&input_devices_poll_wait);
+#endif
+}
+
+static int input_open_file(struct inode *inode, struct file *file)
+{
+ struct input_handler *handler = input_table[iminor(inode) >> 5];
+ struct file_operations *old_fops, *new_fops = NULL;
+ int err;
+
+ /* No load-on-demand here? */
+ if (!handler || !(new_fops = fops_get(handler->fops)))
+ return -ENODEV;
+
+ /*
+ * That's _really_ odd. Usually NULL ->open means "nothing special",
+ * not "no device". Oh, well...
+ */
+ if (!new_fops->open) {
+ fops_put(new_fops);
+ return -ENODEV;
+ }
+ old_fops = file->f_op;
+ file->f_op = new_fops;
+
+ err = new_fops->open(inode, file);
+
+ if (err) {
+ fops_put(file->f_op);
+ file->f_op = fops_get(old_fops);
+ }
+ fops_put(old_fops);
+ return err;
+}
+
+static struct file_operations input_fops = {
+ .owner = THIS_MODULE,
+ .open = input_open_file,
+};
+
+#ifdef CONFIG_PROC_FS
+
+#define SPRINTF_BIT_B(bit, name, max) \
+ do { \
+ len += sprintf(buf + len, "B: %s", name); \
+ for (i = NBITS(max) - 1; i >= 0; i--) \
+ if (dev->bit[i]) break; \
+ for (; i >= 0; i--) \
+ len += sprintf(buf + len, "%lx ", dev->bit[i]); \
+ len += sprintf(buf + len, "\n"); \
+ } while (0)
+
+#define SPRINTF_BIT_B2(bit, name, max, ev) \
+ do { \
+ if (test_bit(ev, dev->evbit)) \
+ SPRINTF_BIT_B(bit, name, max); \
+ } while (0)
+
+
+static unsigned int input_devices_poll(struct file *file, poll_table *wait)
+{
+ int state = input_devices_state;
+ poll_wait(file, &input_devices_poll_wait, wait);
+ if (state != input_devices_state)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
+{
+ struct input_dev *dev;
+ struct input_handle *handle;
+
+ off_t at = 0;
+ int i, len, cnt = 0;
+
+ list_for_each_entry(dev, &input_dev_list, node) {
+
+ len = sprintf(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
+ dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
+
+ len += sprintf(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
+ len += sprintf(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : "");
+ len += sprintf(buf + len, "H: Handlers=");
+
+ list_for_each_entry(handle, &dev->h_list, d_node)
+ len += sprintf(buf + len, "%s ", handle->name);
+
+ len += sprintf(buf + len, "\n");
+
+ SPRINTF_BIT_B(evbit, "EV=", EV_MAX);
+ SPRINTF_BIT_B2(keybit, "KEY=", KEY_MAX, EV_KEY);
+ SPRINTF_BIT_B2(relbit, "REL=", REL_MAX, EV_REL);
+ SPRINTF_BIT_B2(absbit, "ABS=", ABS_MAX, EV_ABS);
+ SPRINTF_BIT_B2(mscbit, "MSC=", MSC_MAX, EV_MSC);
+ SPRINTF_BIT_B2(ledbit, "LED=", LED_MAX, EV_LED);
+ SPRINTF_BIT_B2(sndbit, "SND=", SND_MAX, EV_SND);
+ SPRINTF_BIT_B2(ffbit, "FF=", FF_MAX, EV_FF);
+
+ len += sprintf(buf + len, "\n");
+
+ at += len;
+
+ if (at >= pos) {
+ if (!*start) {
+ *start = buf + (pos - (at - len));
+ cnt = at - pos;
+ } else cnt += len;
+ buf += len;
+ if (cnt >= count)
+ break;
+ }
+ }
+
+ if (&dev->node == &input_dev_list)
+ *eof = 1;
+
+ return (count > cnt) ? cnt : count;
+}
+
+static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
+{
+ struct input_handler *handler;
+
+ off_t at = 0;
+ int len = 0, cnt = 0;
+ int i = 0;
+
+ list_for_each_entry(handler, &input_handler_list, node) {
+
+ if (handler->fops)
+ len = sprintf(buf, "N: Number=%d Name=%s Minor=%d\n",
+ i++, handler->name, handler->minor);
+ else
+ len = sprintf(buf, "N: Number=%d Name=%s\n",
+ i++, handler->name);
+
+ at += len;
+
+ if (at >= pos) {
+ if (!*start) {
+ *start = buf + (pos - (at - len));
+ cnt = at - pos;
+ } else cnt += len;
+ buf += len;
+ if (cnt >= count)
+ break;
+ }
+ }
+ if (&handler->node == &input_handler_list)
+ *eof = 1;
+
+ return (count > cnt) ? cnt : count;
+}
+
+static int __init input_proc_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ proc_bus_input_dir = proc_mkdir("input", proc_bus);
+ if (proc_bus_input_dir == NULL)
+ return -ENOMEM;
+ proc_bus_input_dir->owner = THIS_MODULE;
+ entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL);
+ if (entry == NULL) {
+ remove_proc_entry("input", proc_bus);
+ return -ENOMEM;
+ }
+ entry->owner = THIS_MODULE;
+ entry->proc_fops->poll = input_devices_poll;
+ entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL);
+ if (entry == NULL) {
+ remove_proc_entry("devices", proc_bus_input_dir);
+ remove_proc_entry("input", proc_bus);
+ return -ENOMEM;
+ }
+ entry->owner = THIS_MODULE;
+ return 0;
+}
+#else /* !CONFIG_PROC_FS */
+static inline int input_proc_init(void) { return 0; }
+#endif
+
+struct class_simple *input_class;
+
+static int __init input_init(void)
+{
+ int retval = -ENOMEM;
+
+ input_class = class_simple_create(THIS_MODULE, "input");
+ if (IS_ERR(input_class))
+ return PTR_ERR(input_class);
+ input_proc_init();
+ retval = register_chrdev(INPUT_MAJOR, "input", &input_fops);
+ if (retval) {
+ printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
+ remove_proc_entry("devices", proc_bus_input_dir);
+ remove_proc_entry("handlers", proc_bus_input_dir);
+ remove_proc_entry("input", proc_bus);
+ class_simple_destroy(input_class);
+ return retval;
+ }
+
+ retval = devfs_mk_dir("input");
+ if (retval) {
+ remove_proc_entry("devices", proc_bus_input_dir);
+ remove_proc_entry("handlers", proc_bus_input_dir);
+ remove_proc_entry("input", proc_bus);
+ unregister_chrdev(INPUT_MAJOR, "input");
+ class_simple_destroy(input_class);
+ }
+ return retval;
+}
+
+static void __exit input_exit(void)
+{
+ remove_proc_entry("devices", proc_bus_input_dir);
+ remove_proc_entry("handlers", proc_bus_input_dir);
+ remove_proc_entry("input", proc_bus);
+
+ devfs_remove("input");
+ unregister_chrdev(INPUT_MAJOR, "input");
+ class_simple_destroy(input_class);
+}
+
+subsys_initcall(input_init);
+module_exit(input_exit);
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
new file mode 100644
index 000000000000..7d7527f8b02d
--- /dev/null
+++ b/drivers/input/joydev.c
@@ -0,0 +1,533 @@
+/*
+ * Joystick device driver for the input driver suite.
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 1999 Colin Van Dyke
+ *
+ * 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.
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Joystick device interfaces");
+MODULE_SUPPORTED_DEVICE("input/js");
+MODULE_LICENSE("GPL");
+
+#define JOYDEV_MINOR_BASE 0
+#define JOYDEV_MINORS 16
+#define JOYDEV_BUFFER_SIZE 64
+
+#define MSECS(t) (1000 * ((t) / HZ) + 1000 * ((t) % HZ) / HZ)
+
+struct joydev {
+ int exist;
+ int open;
+ int minor;
+ char name[16];
+ struct input_handle handle;
+ wait_queue_head_t wait;
+ struct list_head list;
+ struct js_corr corr[ABS_MAX + 1];
+ struct JS_DATA_SAVE_TYPE glue;
+ int nabs;
+ int nkey;
+ __u16 keymap[KEY_MAX - BTN_MISC + 1];
+ __u16 keypam[KEY_MAX - BTN_MISC + 1];
+ __u8 absmap[ABS_MAX + 1];
+ __u8 abspam[ABS_MAX + 1];
+ __s16 abs[ABS_MAX + 1];
+};
+
+struct joydev_list {
+ struct js_event buffer[JOYDEV_BUFFER_SIZE];
+ int head;
+ int tail;
+ int startup;
+ struct fasync_struct *fasync;
+ struct joydev *joydev;
+ struct list_head node;
+};
+
+static struct joydev *joydev_table[JOYDEV_MINORS];
+
+static int joydev_correct(int value, struct js_corr *corr)
+{
+ switch (corr->type) {
+ case JS_CORR_NONE:
+ break;
+ case JS_CORR_BROKEN:
+ value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
+ ((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
+ ((corr->coef[2] * (value - corr->coef[0])) >> 14);
+ break;
+ default:
+ return 0;
+ }
+
+ if (value < -32767) return -32767;
+ if (value > 32767) return 32767;
+
+ return value;
+}
+
+static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+ struct joydev *joydev = handle->private;
+ struct joydev_list *list;
+ struct js_event event;
+
+ switch (type) {
+
+ case EV_KEY:
+ if (code < BTN_MISC || value == 2) return;
+ event.type = JS_EVENT_BUTTON;
+ event.number = joydev->keymap[code - BTN_MISC];
+ event.value = value;
+ break;
+
+ case EV_ABS:
+ event.type = JS_EVENT_AXIS;
+ event.number = joydev->absmap[code];
+ event.value = joydev_correct(value, joydev->corr + event.number);
+ if (event.value == joydev->abs[event.number]) return;
+ joydev->abs[event.number] = event.value;
+ break;
+
+ default:
+ return;
+ }
+
+ event.time = MSECS(jiffies);
+
+ list_for_each_entry(list, &joydev->list, node) {
+
+ memcpy(list->buffer + list->head, &event, sizeof(struct js_event));
+
+ if (list->startup == joydev->nabs + joydev->nkey)
+ if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
+ list->startup = 0;
+
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ }
+
+ wake_up_interruptible(&joydev->wait);
+}
+
+static int joydev_fasync(int fd, struct file *file, int on)
+{
+ int retval;
+ struct joydev_list *list = file->private_data;
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+static void joydev_free(struct joydev *joydev)
+{
+ joydev_table[joydev->minor] = NULL;
+ kfree(joydev);
+}
+
+static int joydev_release(struct inode * inode, struct file * file)
+{
+ struct joydev_list *list = file->private_data;
+
+ joydev_fasync(-1, file, 0);
+
+ list_del(&list->node);
+
+ if (!--list->joydev->open) {
+ if (list->joydev->exist)
+ input_close_device(&list->joydev->handle);
+ else
+ joydev_free(list->joydev);
+ }
+
+ kfree(list);
+ return 0;
+}
+
+static int joydev_open(struct inode *inode, struct file *file)
+{
+ struct joydev_list *list;
+ int i = iminor(inode) - JOYDEV_MINOR_BASE;
+
+ if (i >= JOYDEV_MINORS || !joydev_table[i])
+ return -ENODEV;
+
+ if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(list, 0, sizeof(struct joydev_list));
+
+ list->joydev = joydev_table[i];
+ list_add_tail(&list->node, &joydev_table[i]->list);
+ file->private_data = list;
+
+ if (!list->joydev->open++)
+ if (list->joydev->exist)
+ input_open_device(&list->joydev->handle);
+
+ return 0;
+}
+
+static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct joydev_list *list = file->private_data;
+ struct joydev *joydev = list->joydev;
+ struct input_dev *input = joydev->handle.dev;
+ int retval = 0;
+
+ if (!list->joydev->exist)
+ return -ENODEV;
+
+ if (count < sizeof(struct js_event))
+ return -EINVAL;
+
+ if (count == sizeof(struct JS_DATA_TYPE)) {
+
+ struct JS_DATA_TYPE data;
+ int i;
+
+ for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
+ data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
+ data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
+ data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
+
+ if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
+ return -EFAULT;
+
+ list->startup = 0;
+ list->tail = list->head;
+
+ return sizeof(struct JS_DATA_TYPE);
+ }
+
+ if (list->startup == joydev->nabs + joydev->nkey
+ && list->head == list->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(list->joydev->wait,
+ !list->joydev->exist ||
+ list->startup < joydev->nabs + joydev->nkey ||
+ list->head != list->tail);
+
+ if (retval)
+ return retval;
+
+ if (!list->joydev->exist)
+ return -ENODEV;
+
+ while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
+
+ struct js_event event;
+
+ event.time = MSECS(jiffies);
+
+ if (list->startup < joydev->nkey) {
+ event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+ event.number = list->startup;
+ event.value = !!test_bit(joydev->keypam[event.number], input->key);
+ } else {
+ event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+ event.number = list->startup - joydev->nkey;
+ event.value = joydev->abs[event.number];
+ }
+
+ if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
+ return -EFAULT;
+
+ list->startup++;
+ retval += sizeof(struct js_event);
+ }
+
+ while (list->head != list->tail && retval + sizeof(struct js_event) <= count) {
+
+ if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event)))
+ return -EFAULT;
+
+ list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
+ retval += sizeof(struct js_event);
+ }
+
+ return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int joydev_poll(struct file *file, poll_table *wait)
+{
+ struct joydev_list *list = file->private_data;
+ poll_wait(file, &list->joydev->wait, wait);
+ return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ?
+ (POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR));
+}
+
+static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct joydev_list *list = file->private_data;
+ struct joydev *joydev = list->joydev;
+ struct input_dev *dev = joydev->handle.dev;
+ void __user *argp = (void __user *)arg;
+ int i, j;
+
+ if (!joydev->exist) return -ENODEV;
+
+ switch (cmd) {
+
+ case JS_SET_CAL:
+ return copy_from_user(&joydev->glue.JS_CORR, argp,
+ sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+ case JS_GET_CAL:
+ return copy_to_user(argp, &joydev->glue.JS_CORR,
+ sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+ case JS_SET_TIMEOUT:
+ return get_user(joydev->glue.JS_TIMEOUT, (int __user *) arg);
+ case JS_GET_TIMEOUT:
+ return put_user(joydev->glue.JS_TIMEOUT, (int __user *) arg);
+ case JS_SET_TIMELIMIT:
+ return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
+ case JS_GET_TIMELIMIT:
+ return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
+ case JS_SET_ALL:
+ return copy_from_user(&joydev->glue, argp,
+ sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+ case JS_GET_ALL:
+ return copy_to_user(argp, &joydev->glue,
+ sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+
+ case JSIOCGVERSION:
+ return put_user(JS_VERSION, (__u32 __user *) arg);
+ case JSIOCGAXES:
+ return put_user(joydev->nabs, (__u8 __user *) arg);
+ case JSIOCGBUTTONS:
+ return put_user(joydev->nkey, (__u8 __user *) arg);
+ case JSIOCSCORR:
+ if (copy_from_user(joydev->corr, argp,
+ sizeof(struct js_corr) * joydev->nabs))
+ return -EFAULT;
+ for (i = 0; i < joydev->nabs; i++) {
+ j = joydev->abspam[i];
+ joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
+ }
+ return 0;
+ case JSIOCGCORR:
+ return copy_to_user(argp, joydev->corr,
+ sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0;
+ case JSIOCSAXMAP:
+ if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1)))
+ return -EFAULT;
+ for (i = 0; i < joydev->nabs; i++) {
+ if (joydev->abspam[i] > ABS_MAX) return -EINVAL;
+ joydev->absmap[joydev->abspam[i]] = i;
+ }
+ return 0;
+ case JSIOCGAXMAP:
+ return copy_to_user(argp, joydev->abspam,
+ sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
+ case JSIOCSBTNMAP:
+ if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
+ return -EFAULT;
+ for (i = 0; i < joydev->nkey; i++) {
+ if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) return -EINVAL;
+ joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
+ }
+ return 0;
+ case JSIOCGBTNMAP:
+ return copy_to_user(argp, joydev->keypam,
+ sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
+ default:
+ if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+ int len;
+ if (!dev->name) return 0;
+ len = strlen(dev->name) + 1;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ if (copy_to_user(argp, dev->name, len)) return -EFAULT;
+ return len;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct file_operations joydev_fops = {
+ .owner = THIS_MODULE,
+ .read = joydev_read,
+ .write = joydev_write,
+ .poll = joydev_poll,
+ .open = joydev_open,
+ .release = joydev_release,
+ .ioctl = joydev_ioctl,
+ .fasync = joydev_fasync,
+};
+
+static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+{
+ struct joydev *joydev;
+ int i, j, t, minor;
+
+ for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
+ if (minor == JOYDEV_MINORS) {
+ printk(KERN_ERR "joydev: no more free joydev devices\n");
+ return NULL;
+ }
+
+ if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL)))
+ return NULL;
+ memset(joydev, 0, sizeof(struct joydev));
+
+ INIT_LIST_HEAD(&joydev->list);
+ init_waitqueue_head(&joydev->wait);
+
+ joydev->minor = minor;
+ joydev->exist = 1;
+ joydev->handle.dev = dev;
+ joydev->handle.name = joydev->name;
+ joydev->handle.handler = handler;
+ joydev->handle.private = joydev;
+ sprintf(joydev->name, "js%d", minor);
+
+ for (i = 0; i < ABS_MAX + 1; i++)
+ if (test_bit(i, dev->absbit)) {
+ joydev->absmap[i] = joydev->nabs;
+ joydev->abspam[joydev->nabs] = i;
+ joydev->nabs++;
+ }
+
+ for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++)
+ if (test_bit(i + BTN_MISC, dev->keybit)) {
+ joydev->keymap[i] = joydev->nkey;
+ joydev->keypam[joydev->nkey] = i + BTN_MISC;
+ joydev->nkey++;
+ }
+
+ for (i = 0; i < BTN_JOYSTICK - BTN_MISC + 1; i++)
+ if (test_bit(i + BTN_MISC, dev->keybit)) {
+ joydev->keymap[i] = joydev->nkey;
+ joydev->keypam[joydev->nkey] = i + BTN_MISC;
+ joydev->nkey++;
+ }
+
+ for (i = 0; i < joydev->nabs; i++) {
+ j = joydev->abspam[i];
+ if (dev->absmax[j] == dev->absmin[j]) {
+ joydev->corr[i].type = JS_CORR_NONE;
+ joydev->abs[i] = dev->abs[j];
+ continue;
+ }
+ joydev->corr[i].type = JS_CORR_BROKEN;
+ joydev->corr[i].prec = dev->absfuzz[j];
+ joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
+ joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
+ if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j])))
+ continue;
+ joydev->corr[i].coef[2] = (1 << 29) / t;
+ joydev->corr[i].coef[3] = (1 << 29) / t;
+
+ joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
+ }
+
+ joydev_table[minor] = joydev;
+
+ devfs_mk_cdev(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor),
+ S_IFCHR|S_IRUGO|S_IWUSR, "input/js%d", minor);
+ class_simple_device_add(input_class,
+ MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor),
+ dev->dev, "js%d", minor);
+
+ return &joydev->handle;
+}
+
+static void joydev_disconnect(struct input_handle *handle)
+{
+ struct joydev *joydev = handle->private;
+ struct joydev_list *list;
+
+ class_simple_device_remove(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor));
+ devfs_remove("input/js%d", joydev->minor);
+ joydev->exist = 0;
+
+ if (joydev->open) {
+ input_close_device(handle);
+ wake_up_interruptible(&joydev->wait);
+ list_for_each_entry(list, &joydev->list, node)
+ kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+ } else
+ joydev_free(joydev);
+}
+
+static struct input_device_id joydev_blacklist[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+ .evbit = { BIT(EV_KEY) },
+ .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+ }, /* Avoid itouchpads, touchscreens and tablets */
+ { }, /* Terminating entry */
+};
+
+static struct input_device_id joydev_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT(EV_ABS) },
+ .absbit = { BIT(ABS_X) },
+ },
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT(EV_ABS) },
+ .absbit = { BIT(ABS_WHEEL) },
+ },
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT(EV_ABS) },
+ .absbit = { BIT(ABS_THROTTLE) },
+ },
+ { }, /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, joydev_ids);
+
+static struct input_handler joydev_handler = {
+ .event = joydev_event,
+ .connect = joydev_connect,
+ .disconnect = joydev_disconnect,
+ .fops = &joydev_fops,
+ .minor = JOYDEV_MINOR_BASE,
+ .name = "joydev",
+ .id_table = joydev_ids,
+ .blacklist = joydev_blacklist,
+};
+
+static int __init joydev_init(void)
+{
+ input_register_handler(&joydev_handler);
+ return 0;
+}
+
+static void __exit joydev_exit(void)
+{
+ input_unregister_handler(&joydev_handler);
+}
+
+module_init(joydev_init);
+module_exit(joydev_exit);
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
new file mode 100644
index 000000000000..67519ef0ef95
--- /dev/null
+++ b/drivers/input/joystick/Kconfig
@@ -0,0 +1,256 @@
+#
+# Joystick driver configuration
+#
+menuconfig INPUT_JOYSTICK
+ bool "Joysticks"
+ help
+ 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
+ doesn't affect the kernel.
+
+ Please read the file <file:Documentation/input/joystick.txt> which
+ contains more information.
+
+if INPUT_JOYSTICK
+
+config JOYSTICK_ANALOG
+ tristate "Classic PC analog joysticks and gamepads"
+ select GAMEPORT
+ ---help---
+ Say Y here if you have a joystick that connects to the PC
+ gameport. In addition to the usual PC analog joystick, this driver
+ supports many extensions, including joysticks with throttle control,
+ with rudders, additional hats and buttons compatible with CH
+ Flightstick Pro, ThrustMaster FCS, 6 and 8 button gamepads, or
+ Saitek Cyborg joysticks.
+
+ Please read the file <file:Documentation/input/joystick.txt> which
+ contains more information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called analog.
+
+config JOYSTICK_A3D
+ tristate "Assasin 3D and MadCatz Panther devices"
+ select GAMEPORT
+ help
+ Say Y here if you have an FPGaming or MadCatz controller using the
+ A3D protocol over the PC gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called a3d.
+
+config JOYSTICK_ADI
+ tristate "Logitech ADI digital joysticks and gamepads"
+ select GAMEPORT
+ help
+ Say Y here if you have a Logitech controller using the ADI
+ protocol over the PC gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adi.
+
+config JOYSTICK_COBRA
+ tristate "Creative Labs Blaster Cobra gamepad"
+ select GAMEPORT
+ help
+ Say Y here if you have a Creative Labs Blaster Cobra gamepad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cobra.
+
+config JOYSTICK_GF2K
+ tristate "Genius Flight2000 Digital joysticks and gamepads"
+ select GAMEPORT
+ help
+ Say Y here if you have a Genius Flight2000 or MaxFighter digitally
+ communicating joystick or gamepad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gf2k.
+
+config JOYSTICK_GRIP
+ tristate "Gravis GrIP joysticks and gamepads"
+ select GAMEPORT
+ help
+ Say Y here if you have a Gravis controller using the GrIP protocol
+ over the PC gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called grip.
+
+config JOYSTICK_GRIP_MP
+ tristate "Gravis GrIP MultiPort"
+ select GAMEPORT
+ help
+ Say Y here if you have the original Gravis GrIP MultiPort, a hub
+ that connects to the gameport and you connect gamepads to it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called grip_mp.
+
+config JOYSTICK_GUILLEMOT
+ tristate "Guillemot joysticks and gamepads"
+ select GAMEPORT
+ help
+ Say Y here if you have a Guillemot joystick using a digital
+ protocol over the PC gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called guillemot.
+
+config JOYSTICK_INTERACT
+ tristate "InterAct digital joysticks and gamepads"
+ select GAMEPORT
+ help
+ Say Y here if you have an InterAct gameport or joystick
+ communicating digitally over the gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called interact.
+
+config JOYSTICK_SIDEWINDER
+ tristate "Microsoft SideWinder digital joysticks and gamepads"
+ select GAMEPORT
+ help
+ Say Y here if you have a Microsoft controller using the Digital
+ Overdrive protocol over PC gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sidewinder.
+
+config JOYSTICK_TMDC
+ tristate "ThrustMaster DirectConnect joysticks and gamepads"
+ select GAMEPORT
+ help
+ Say Y here if you have a ThrustMaster controller using the
+ DirectConnect (BSP) protocol over the PC gameport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tmdc.
+
+source "drivers/input/joystick/iforce/Kconfig"
+
+config JOYSTICK_WARRIOR
+ tristate "Logitech WingMan Warrior joystick"
+ select SERIO
+ help
+ Say Y here if you have a Logitech WingMan Warrior joystick connected
+ to your computer's serial port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called warrior.
+
+config JOYSTICK_MAGELLAN
+ tristate "LogiCad3d Magellan/SpaceMouse 6dof controllers"
+ select SERIO
+ help
+ Say Y here if you have a Magellan or Space Mouse 6DOF controller
+ connected to your computer's serial port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called magellan.
+
+config JOYSTICK_SPACEORB
+ tristate "SpaceTec SpaceOrb/Avenger 6dof controllers"
+ select SERIO
+ help
+ Say Y here if you have a SpaceOrb 360 or SpaceBall Avenger 6DOF
+ controller connected to your computer's serial port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spaceorb.
+
+config JOYSTICK_SPACEBALL
+ tristate "SpaceTec SpaceBall 6dof controllers"
+ select SERIO
+ help
+ Say Y here if you have a SpaceTec SpaceBall 2003/3003/4000 FLX
+ controller connected to your computer's serial port. For the
+ SpaceBall 4000 USB model, use the USB HID driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spaceball.
+
+config JOYSTICK_STINGER
+ tristate "Gravis Stinger gamepad"
+ select SERIO
+ help
+ Say Y here if you have a Gravis Stinger connected to one of your
+ serial ports.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stinger.
+
+config JOYSTICK_TWIDJOY
+ tristate "Twiddler as a joystick"
+ select SERIO
+ help
+ Say Y here if you have a Handykey Twiddler connected to your
+ computer's serial port and want to use it as a joystick.
+
+ To compile this driver as a module, choose M here: the
+ module will be called twidjoy.
+
+config JOYSTICK_DB9
+ tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads"
+ depends on PARPORT
+ ---help---
+ Say Y here if you have a Sega Master System gamepad, Sega Genesis
+ gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
+ Commodore, Amstrad CPC joystick connected to your parallel port.
+ For more information on how to use the driver please read
+ <file:Documentation/input/joystick-parport.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called db9.
+
+config JOYSTICK_GAMECON
+ tristate "Multisystem, NES, SNES, N64, PSX joysticks and gamepads"
+ depends on PARPORT
+ ---help---
+ Say Y here if you have a Nintendo Entertainment System gamepad,
+ Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad,
+ Sony PlayStation gamepad or a Multisystem -- Atari, Amiga,
+ Commodore, Amstrad CPC joystick connected to your parallel port.
+ For more information on how to use the driver please read
+ <file:Documentation/input/joystick-parport.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gamecon.
+
+config JOYSTICK_TURBOGRAFX
+ tristate "Multisystem joysticks via TurboGraFX device"
+ depends on PARPORT
+ help
+ Say Y here if you have the TurboGraFX interface by Steffen Schwenke,
+ and want to use it with Multisystem -- Atari, Amiga, Commodore,
+ Amstrad CPC joystick. For more information on how to use the driver
+ please read <file:Documentation/input/joystick-parport.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called turbografx.
+
+config JOYSTICK_AMIGA
+ tristate "Amiga joysticks"
+ depends on AMIGA
+ help
+ Say Y here if you have an Amiga with a digital joystick connected
+ to it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called amijoy.
+
+config JOYSTICK_JOYDUMP
+ tristate "Gameport data dumper"
+ select GAMEPORT
+ help
+ 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.
+
+ To compile this driver as a module, choose M here: the
+ module will be called joydump.
+
+endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
new file mode 100644
index 000000000000..5231f6ff75b8
--- /dev/null
+++ b/drivers/input/joystick/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
+obj-$(CONFIG_JOYSTICK_ADI) += adi.o
+obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
+obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o
+obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o
+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_GRIP_MP) += grip_mp.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
+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/
diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c
new file mode 100644
index 000000000000..ad39fe4bf35f
--- /dev/null
+++ b/drivers/input/joystick/a3d.c
@@ -0,0 +1,417 @@
+/*
+ * $Id: a3d.c,v 1.21 2002/01/22 20:11:50 vojtech Exp $
+ *
+ * Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * FP-Gaming Assasin 3D joystick 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/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC "FP-Gaming Assasin 3D joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define A3D_MAX_START 600 /* 600 us */
+#define A3D_MAX_STROBE 80 /* 80 us */
+#define A3D_MAX_LENGTH 40 /* 40*3 bits */
+
+#define A3D_MODE_A3D 1 /* Assassin 3D */
+#define A3D_MODE_PAN 2 /* Panther */
+#define A3D_MODE_OEM 3 /* Panther OEM version */
+#define A3D_MODE_PXL 4 /* Panther XL */
+
+static char *a3d_names[] = { NULL, "FP-Gaming Assassin 3D", "MadCatz Panther", "OEM Panther",
+ "MadCatz Panther XL", "MadCatz Panther XL w/ rudder" };
+
+struct a3d {
+ struct gameport *gameport;
+ struct gameport *adc;
+ struct input_dev dev;
+ int axes[4];
+ int buttons;
+ int mode;
+ int length;
+ int reads;
+ int bads;
+ char phys[32];
+};
+
+/*
+ * a3d_read_packet() reads an Assassin 3D packet.
+ */
+
+static int a3d_read_packet(struct gameport *gameport, int length, char *data)
+{
+ unsigned long flags;
+ unsigned char u, v;
+ unsigned int t, s;
+ int i;
+
+ i = 0;
+ t = gameport_time(gameport, A3D_MAX_START);
+ s = gameport_time(gameport, A3D_MAX_STROBE);
+
+ local_irq_save(flags);
+ gameport_trigger(gameport);
+ v = gameport_read(gameport);
+
+ while (t > 0 && i < length) {
+ t--;
+ u = v; v = gameport_read(gameport);
+ if (~v & u & 0x10) {
+ data[i++] = v >> 5;
+ t = s;
+ }
+ }
+
+ local_irq_restore(flags);
+
+ return i;
+}
+
+/*
+ * a3d_csum() computes checksum of triplet packet
+ */
+
+static int a3d_csum(char *data, int count)
+{
+ int i, csum = 0;
+
+ for (i = 0; i < count - 2; i++)
+ csum += data[i];
+ return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]);
+}
+
+static void a3d_read(struct a3d *a3d, unsigned char *data)
+{
+ struct input_dev *dev = &a3d->dev;
+
+ switch (a3d->mode) {
+
+ case A3D_MODE_A3D:
+ case A3D_MODE_OEM:
+ case A3D_MODE_PAN:
+
+ input_report_rel(dev, REL_X, ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7));
+ input_report_rel(dev, REL_Y, ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7));
+
+ input_report_key(dev, BTN_RIGHT, data[2] & 1);
+ input_report_key(dev, BTN_LEFT, data[3] & 2);
+ input_report_key(dev, BTN_MIDDLE, data[3] & 4);
+
+ input_sync(dev);
+
+ a3d->axes[0] = ((signed char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128;
+ a3d->axes[1] = ((signed char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128;
+ a3d->axes[2] = ((signed char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128;
+ a3d->axes[3] = ((signed char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128;
+
+ a3d->buttons = ((data[3] << 3) | data[4]) & 0xf;
+
+ break;
+
+ case A3D_MODE_PXL:
+
+ input_report_rel(dev, REL_X, ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7));
+ input_report_rel(dev, REL_Y, ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7));
+
+ input_report_key(dev, BTN_RIGHT, data[2] & 1);
+ input_report_key(dev, BTN_LEFT, data[3] & 2);
+ input_report_key(dev, BTN_MIDDLE, data[3] & 4);
+ input_report_key(dev, BTN_SIDE, data[7] & 2);
+ input_report_key(dev, BTN_EXTRA, data[7] & 4);
+
+ input_report_abs(dev, ABS_X, ((signed char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128);
+ input_report_abs(dev, ABS_Y, ((signed char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128);
+ input_report_abs(dev, ABS_RUDDER, ((signed char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128);
+ input_report_abs(dev, ABS_THROTTLE, ((signed char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128);
+
+ input_report_abs(dev, ABS_HAT0X, ( data[5] & 1) - ((data[5] >> 2) & 1));
+ input_report_abs(dev, ABS_HAT0Y, ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1));
+ input_report_abs(dev, ABS_HAT1X, ((data[4] >> 1) & 1) - ( data[3] & 1));
+ input_report_abs(dev, ABS_HAT1Y, ((data[4] >> 2) & 1) - ( data[4] & 1));
+
+ input_report_key(dev, BTN_TRIGGER, data[8] & 1);
+ input_report_key(dev, BTN_THUMB, data[8] & 2);
+ input_report_key(dev, BTN_TOP, data[8] & 4);
+ input_report_key(dev, BTN_PINKIE, data[7] & 1);
+
+ input_sync(dev);
+
+ break;
+ }
+}
+
+
+/*
+ * a3d_poll() reads and analyzes A3D joystick data.
+ */
+
+static void a3d_poll(struct gameport *gameport)
+{
+ struct a3d *a3d = gameport_get_drvdata(gameport);
+ unsigned char data[A3D_MAX_LENGTH];
+
+ a3d->reads++;
+ if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length ||
+ data[0] != a3d->mode || a3d_csum(data, a3d->length))
+ a3d->bads++;
+ else
+ a3d_read(a3d, data);
+}
+
+/*
+ * a3d_adc_cooked_read() copies the acis and button data to the
+ * callers arrays. It could do the read itself, but the caller could
+ * call this more than 50 times a second, which would use too much CPU.
+ */
+
+static int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ struct a3d *a3d = gameport->port_data;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1;
+ *buttons = a3d->buttons;
+ return 0;
+}
+
+/*
+ * a3d_adc_open() is the gameport open routine. It refuses to serve
+ * any but cooked data.
+ */
+
+static int a3d_adc_open(struct gameport *gameport, int mode)
+{
+ struct a3d *a3d = gameport->port_data;
+
+ if (mode != GAMEPORT_MODE_COOKED)
+ return -1;
+
+ gameport_start_polling(a3d->gameport);
+ return 0;
+}
+
+/*
+ * a3d_adc_close() is a callback from the input close routine.
+ */
+
+static void a3d_adc_close(struct gameport *gameport)
+{
+ struct a3d *a3d = gameport->port_data;
+
+ gameport_stop_polling(a3d->gameport);
+}
+
+/*
+ * a3d_open() is a callback from the input open routine.
+ */
+
+static int a3d_open(struct input_dev *dev)
+{
+ struct a3d *a3d = dev->private;
+
+ gameport_start_polling(a3d->gameport);
+ return 0;
+}
+
+/*
+ * a3d_close() is a callback from the input close routine.
+ */
+
+static void a3d_close(struct input_dev *dev)
+{
+ struct a3d *a3d = dev->private;
+
+ gameport_stop_polling(a3d->gameport);
+}
+
+/*
+ * a3d_connect() probes for A3D joysticks.
+ */
+
+static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct a3d *a3d;
+ struct gameport *adc;
+ unsigned char data[A3D_MAX_LENGTH];
+ int i;
+ int err;
+
+ if (!(a3d = kcalloc(1, sizeof(struct a3d), GFP_KERNEL)))
+ return -ENOMEM;
+
+ a3d->gameport = gameport;
+
+ gameport_set_drvdata(gameport, a3d);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ i = a3d_read_packet(gameport, A3D_MAX_LENGTH, data);
+
+ if (!i || a3d_csum(data, i)) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ a3d->mode = data[0];
+
+ if (!a3d->mode || a3d->mode > 5) {
+ printk(KERN_WARNING "a3d.c: Unknown A3D device detected "
+ "(%s, id=%d), contact <vojtech@ucw.cz>\n", gameport->phys, a3d->mode);
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ gameport_set_poll_handler(gameport, a3d_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ sprintf(a3d->phys, "%s/input0", gameport->phys);
+
+ if (a3d->mode == A3D_MODE_PXL) {
+
+ int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER };
+
+ a3d->length = 33;
+
+ init_input_dev(&a3d->dev);
+
+ a3d->dev.evbit[0] |= BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL);
+ a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y);
+ a3d->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_RUDDER)
+ | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) | BIT(ABS_HAT1X) | BIT(ABS_HAT1Y);
+
+ a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE)
+ | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+
+ a3d->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_PINKIE);
+
+ a3d_read(a3d, data);
+
+ for (i = 0; i < 4; i++) {
+ if (i < 2)
+ input_set_abs_params(&a3d->dev, axes[i], 48, a3d->dev.abs[axes[i]] * 2 - 48, 0, 8);
+ else
+ input_set_abs_params(&a3d->dev, axes[i], 2, 253, 0, 0);
+ input_set_abs_params(&a3d->dev, ABS_HAT0X + i, -1, 1, 0, 0);
+ }
+
+ } else {
+ a3d->length = 29;
+
+ init_input_dev(&a3d->dev);
+
+ a3d->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL);
+ a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y);
+ a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE);
+
+ a3d_read(a3d, data);
+
+ if (!(a3d->adc = adc = gameport_allocate_port()))
+ printk(KERN_ERR "a3d: Not enough memory for ADC port\n");
+ else {
+ adc->port_data = a3d;
+ adc->open = a3d_adc_open;
+ adc->close = a3d_adc_close;
+ adc->cooked_read = a3d_adc_cooked_read;
+ adc->fuzz = 1;
+
+ gameport_set_name(adc, a3d_names[a3d->mode]);
+ gameport_set_phys(adc, "%s/gameport0", gameport->phys);
+ adc->dev.parent = &gameport->dev;
+
+ gameport_register_port(adc);
+ }
+ }
+
+ a3d->dev.private = a3d;
+ a3d->dev.open = a3d_open;
+ a3d->dev.close = a3d_close;
+
+ a3d->dev.name = a3d_names[a3d->mode];
+ a3d->dev.phys = a3d->phys;
+ a3d->dev.id.bustype = BUS_GAMEPORT;
+ a3d->dev.id.vendor = GAMEPORT_ID_VENDOR_MADCATZ;
+ a3d->dev.id.product = a3d->mode;
+ a3d->dev.id.version = 0x0100;
+
+ input_register_device(&a3d->dev);
+ printk(KERN_INFO "input: %s on %s\n", a3d_names[a3d->mode], a3d->phys);
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(a3d);
+ return err;
+}
+
+static void a3d_disconnect(struct gameport *gameport)
+{
+ struct a3d *a3d = gameport_get_drvdata(gameport);
+
+ input_unregister_device(&a3d->dev);
+ if (a3d->adc) {
+ gameport_unregister_port(a3d->adc);
+ a3d->adc = NULL;
+ }
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(a3d);
+}
+
+static struct gameport_driver a3d_drv = {
+ .driver = {
+ .name = "adc",
+ },
+ .description = DRIVER_DESC,
+ .connect = a3d_connect,
+ .disconnect = a3d_disconnect,
+};
+
+static int __init a3d_init(void)
+{
+ gameport_register_driver(&a3d_drv);
+ return 0;
+}
+
+static void __exit a3d_exit(void)
+{
+ gameport_unregister_driver(&a3d_drv);
+}
+
+module_init(a3d_init);
+module_exit(a3d_exit);
diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c
new file mode 100644
index 000000000000..83f6dafc1716
--- /dev/null
+++ b/drivers/input/joystick/adi.c
@@ -0,0 +1,560 @@
+/*
+ * Copyright (c) 1998-2005 Vojtech Pavlik
+ */
+
+/*
+ * Logitech ADI joystick family 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/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Logitech ADI joystick family driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Times, array sizes, flags, ids.
+ */
+
+#define ADI_MAX_START 200 /* Trigger to packet timeout [200us] */
+#define ADI_MAX_STROBE 40 /* Single bit timeout [40us] */
+#define ADI_INIT_DELAY 10 /* Delay after init packet [10ms] */
+#define ADI_DATA_DELAY 4 /* Delay after data packet [4ms] */
+
+#define ADI_MAX_LENGTH 256
+#define ADI_MIN_LENGTH 8
+#define ADI_MIN_LEN_LENGTH 10
+#define ADI_MIN_ID_LENGTH 66
+#define ADI_MAX_NAME_LENGTH 48
+#define ADI_MAX_CNAME_LENGTH 16
+#define ADI_MAX_PHYS_LENGTH 64
+
+#define ADI_FLAG_HAT 0x04
+#define ADI_FLAG_10BIT 0x08
+
+#define ADI_ID_TPD 0x01
+#define ADI_ID_WGP 0x06
+#define ADI_ID_WGPE 0x08
+#define ADI_ID_MAX 0x0a
+
+/*
+ * Names, buttons, axes ...
+ */
+
+static char *adi_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2",
+ "WingMan Interceptor", "WingMan Formula", "WingMan GamePad",
+ "WingMan Extreme Digital 3D", "WingMan GamePad Extreme",
+ "WingMan GamePad USB", "Unknown Device %#x" };
+
+static char adi_wmgpe_abs[] = { ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y };
+static char adi_wmi_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+static char adi_wmed3d_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RZ, ABS_HAT0X, ABS_HAT0Y };
+static char adi_cm2_abs[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+static char adi_wmf_abs[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+
+static short adi_wmgpe_key[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT };
+static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA };
+static short adi_wmed3d_key[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 };
+static short adi_cm2_key[] = { BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 };
+
+static char* adi_abs[] = { adi_wmi_abs, adi_wmgpe_abs, adi_wmf_abs, adi_cm2_abs, adi_wmi_abs, adi_wmf_abs,
+ adi_wmgpe_abs, adi_wmed3d_abs, adi_wmgpe_abs, adi_wmgpe_abs, adi_wmi_abs };
+
+static short* adi_key[] = { adi_wmi_key, adi_wmgpe_key, adi_cm2_key, adi_cm2_key, adi_wmi_key, adi_cm2_key,
+ adi_wmgpe_key, adi_wmed3d_key, adi_wmgpe_key, adi_wmgpe_key, adi_wmi_key };
+
+/*
+ * Hat to axis conversion arrays.
+ */
+
+static struct {
+ int x;
+ int y;
+} adi_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+/*
+ * Per-port information.
+ */
+
+struct adi {
+ struct input_dev dev;
+ int length;
+ int ret;
+ int idx;
+ unsigned char id;
+ char buttons;
+ char axes10;
+ char axes8;
+ signed char pad;
+ char hats;
+ char *abs;
+ short *key;
+ char name[ADI_MAX_NAME_LENGTH];
+ char cname[ADI_MAX_CNAME_LENGTH];
+ char phys[ADI_MAX_PHYS_LENGTH];
+ unsigned char data[ADI_MAX_LENGTH];
+};
+
+struct adi_port {
+ struct gameport *gameport;
+ struct adi adi[2];
+ int bad;
+ int reads;
+};
+
+/*
+ * adi_read_packet() reads a Logitech ADI packet.
+ */
+
+static void adi_read_packet(struct adi_port *port)
+{
+ struct adi *adi = port->adi;
+ struct gameport *gameport = port->gameport;
+ unsigned char u, v, w, x, z;
+ int t[2], s[2], i;
+ unsigned long flags;
+
+ for (i = 0; i < 2; i++) {
+ adi[i].ret = -1;
+ t[i] = gameport_time(gameport, ADI_MAX_START);
+ s[i] = 0;
+ }
+
+ local_irq_save(flags);
+
+ gameport_trigger(gameport);
+ v = z = gameport_read(gameport);
+
+ do {
+ u = v;
+ w = u ^ (v = x = gameport_read(gameport));
+ for (i = 0; i < 2; i++, w >>= 2, x >>= 2) {
+ t[i]--;
+ if ((w & 0x30) && s[i]) {
+ if ((w & 0x30) < 0x30 && adi[i].ret < ADI_MAX_LENGTH && t[i] > 0) {
+ adi[i].data[++adi[i].ret] = w;
+ t[i] = gameport_time(gameport, ADI_MAX_STROBE);
+ } else t[i] = 0;
+ } else if (!(x & 0x30)) s[i] = 1;
+ }
+ } while (t[0] > 0 || t[1] > 0);
+
+ local_irq_restore(flags);
+
+ return;
+}
+
+/*
+ * adi_move_bits() detects a possible 2-stream mode, and moves
+ * the bits accordingly.
+ */
+
+static void adi_move_bits(struct adi_port *port, int length)
+{
+ int i;
+ struct adi *adi = port->adi;
+
+ adi[0].idx = adi[1].idx = 0;
+
+ if (adi[0].ret <= 0 || adi[1].ret <= 0) return;
+ if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return;
+
+ for (i = 1; i <= adi[1].ret; i++)
+ adi[0].data[((length - 1) >> 1) + i + 1] = adi[1].data[i];
+
+ adi[0].ret += adi[1].ret;
+ adi[1].ret = -1;
+}
+
+/*
+ * adi_get_bits() gathers bits from the data packet.
+ */
+
+static inline int adi_get_bits(struct adi *adi, int count)
+{
+ int bits = 0;
+ int i;
+ if ((adi->idx += count) > adi->ret) return 0;
+ for (i = 0; i < count; i++)
+ bits |= ((adi->data[adi->idx - i] >> 5) & 1) << i;
+ return bits;
+}
+
+/*
+ * adi_decode() decodes Logitech joystick data into input events.
+ */
+
+static int adi_decode(struct adi *adi)
+{
+ struct input_dev *dev = &adi->dev;
+ char *abs = adi->abs;
+ short *key = adi->key;
+ int i, t;
+
+ if (adi->ret < adi->length || adi->id != (adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4)))
+ return -1;
+
+ for (i = 0; i < adi->axes10; i++)
+ input_report_abs(dev, *abs++, adi_get_bits(adi, 10));
+
+ for (i = 0; i < adi->axes8; i++)
+ input_report_abs(dev, *abs++, adi_get_bits(adi, 8));
+
+ for (i = 0; i < adi->buttons && i < 63; i++) {
+ if (i == adi->pad) {
+ t = adi_get_bits(adi, 4);
+ input_report_abs(dev, *abs++, ((t >> 2) & 1) - ( t & 1));
+ input_report_abs(dev, *abs++, ((t >> 1) & 1) - ((t >> 3) & 1));
+ }
+ input_report_key(dev, *key++, adi_get_bits(adi, 1));
+ }
+
+ for (i = 0; i < adi->hats; i++) {
+ if ((t = adi_get_bits(adi, 4)) > 8) t = 0;
+ input_report_abs(dev, *abs++, adi_hat_to_axis[t].x);
+ input_report_abs(dev, *abs++, adi_hat_to_axis[t].y);
+ }
+
+ for (i = 63; i < adi->buttons; i++)
+ input_report_key(dev, *key++, adi_get_bits(adi, 1));
+
+ input_sync(dev);
+
+ return 0;
+}
+
+/*
+ * adi_read() reads the data packet and decodes it.
+ */
+
+static int adi_read(struct adi_port *port)
+{
+ int i;
+ int result = 0;
+
+ adi_read_packet(port);
+ adi_move_bits(port, port->adi[0].length);
+
+ for (i = 0; i < 2; i++)
+ if (port->adi[i].length)
+ result |= adi_decode(port->adi + i);
+
+ return result;
+}
+
+/*
+ * adi_poll() repeatedly polls the Logitech joysticks.
+ */
+
+static void adi_poll(struct gameport *gameport)
+{
+ struct adi_port *port = gameport_get_drvdata(gameport);
+
+ port->bad -= adi_read(port);
+ port->reads++;
+}
+
+/*
+ * adi_open() is a callback from the input open routine.
+ */
+
+static int adi_open(struct input_dev *dev)
+{
+ struct adi_port *port = dev->private;
+
+ gameport_start_polling(port->gameport);
+ return 0;
+}
+
+/*
+ * adi_close() is a callback from the input close routine.
+ */
+
+static void adi_close(struct input_dev *dev)
+{
+ struct adi_port *port = dev->private;
+
+ gameport_stop_polling(port->gameport);
+}
+
+/*
+ * adi_init_digital() sends a trigger & delay sequence
+ * to reset and initialize a Logitech joystick into digital mode.
+ */
+
+static void adi_init_digital(struct gameport *gameport)
+{
+ int seq[] = { 4, -2, -3, 10, -6, -11, -7, -9, 11, 0 };
+ int i;
+
+ for (i = 0; seq[i]; i++) {
+ gameport_trigger(gameport);
+ if (seq[i] > 0) msleep(seq[i]);
+ if (seq[i] < 0) {
+ mdelay(-seq[i]);
+ udelay(-seq[i]*14); /* It looks like mdelay() is off by approx 1.4% */
+ }
+ }
+}
+
+static void adi_id_decode(struct adi *adi, struct adi_port *port)
+{
+ int i, t;
+
+ if (adi->ret < ADI_MIN_ID_LENGTH) /* Minimum ID packet length */
+ return;
+
+ if (adi->ret < (t = adi_get_bits(adi, 10))) {
+ printk(KERN_WARNING "adi: Short ID packet: reported: %d != read: %d\n", t, adi->ret);
+ return;
+ }
+
+ adi->id = adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4);
+
+ if ((t = adi_get_bits(adi, 4)) & ADI_FLAG_HAT) adi->hats++;
+
+ adi->length = adi_get_bits(adi, 10);
+
+ if (adi->length >= ADI_MAX_LENGTH || adi->length < ADI_MIN_LENGTH) {
+ printk(KERN_WARNING "adi: Bad data packet length (%d).\n", adi->length);
+ adi->length = 0;
+ return;
+ }
+
+ adi->axes8 = adi_get_bits(adi, 4);
+ adi->buttons = adi_get_bits(adi, 6);
+
+ if (adi_get_bits(adi, 6) != 8 && adi->hats) {
+ printk(KERN_WARNING "adi: Other than 8-dir POVs not supported yet.\n");
+ adi->length = 0;
+ return;
+ }
+
+ adi->buttons += adi_get_bits(adi, 6);
+ adi->hats += adi_get_bits(adi, 4);
+
+ i = adi_get_bits(adi, 4);
+
+ if (t & ADI_FLAG_10BIT) {
+ adi->axes10 = adi->axes8 - i;
+ adi->axes8 = i;
+ }
+
+ t = adi_get_bits(adi, 4);
+
+ for (i = 0; i < t; i++)
+ adi->cname[i] = adi_get_bits(adi, 8);
+ adi->cname[i] = 0;
+
+ t = 8 + adi->buttons + adi->axes10 * 10 + adi->axes8 * 8 + adi->hats * 4;
+ if (adi->length != t && adi->length != t + (t & 1)) {
+ printk(KERN_WARNING "adi: Expected length %d != data length %d\n", t, adi->length);
+ adi->length = 0;
+ return;
+ }
+
+ switch (adi->id) {
+ case ADI_ID_TPD:
+ adi->pad = 4;
+ adi->buttons -= 4;
+ break;
+ case ADI_ID_WGP:
+ adi->pad = 0;
+ adi->buttons -= 4;
+ break;
+ default:
+ adi->pad = -1;
+ break;
+ }
+}
+
+static void adi_init_input(struct adi *adi, struct adi_port *port, int half)
+{
+ int i, t;
+ char buf[ADI_MAX_NAME_LENGTH];
+
+ if (!adi->length) return;
+
+ init_input_dev(&adi->dev);
+
+ t = adi->id < ADI_ID_MAX ? adi->id : ADI_ID_MAX;
+
+ snprintf(buf, ADI_MAX_PHYS_LENGTH, adi_names[t], adi->id);
+ snprintf(adi->name, ADI_MAX_NAME_LENGTH, "Logitech %s", buf);
+ snprintf(adi->phys, ADI_MAX_PHYS_LENGTH, "%s/input%d", port->gameport->phys, half);
+
+ adi->abs = adi_abs[t];
+ adi->key = adi_key[t];
+
+ adi->dev.open = adi_open;
+ adi->dev.close = adi_close;
+
+ adi->dev.name = adi->name;
+ adi->dev.phys = adi->phys;
+ adi->dev.id.bustype = BUS_GAMEPORT;
+ adi->dev.id.vendor = GAMEPORT_ID_VENDOR_LOGITECH;
+ adi->dev.id.product = adi->id;
+ adi->dev.id.version = 0x0100;
+
+ adi->dev.private = port;
+ 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);
+
+ for (i = 0; i < adi->buttons; i++)
+ set_bit(adi->key[i], adi->dev.keybit);
+}
+
+static void adi_init_center(struct adi *adi)
+{
+ int i, t, x;
+
+ if (!adi->length)
+ return;
+
+ for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) {
+
+ t = adi->abs[i];
+ x = adi->dev.abs[t];
+
+ if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE)
+ x = i < adi->axes10 ? 512 : 128;
+
+ if (i < adi->axes10)
+ input_set_abs_params(&adi->dev, t, 64, x * 2 - 64, 2, 16);
+ else if (i < adi->axes10 + adi->axes8)
+ input_set_abs_params(&adi->dev, t, 48, x * 2 - 48, 1, 16);
+ else
+ input_set_abs_params(&adi->dev, t, -1, 1, 0, 0);
+ }
+}
+
+/*
+ * adi_connect() probes for Logitech ADI joysticks.
+ */
+
+static int adi_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct adi_port *port;
+ int i;
+ int err;
+
+ if (!(port = kcalloc(1, sizeof(struct adi_port), GFP_KERNEL)))
+ return -ENOMEM;
+
+ port->gameport = gameport;
+
+ gameport_set_drvdata(gameport, port);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err) {
+ kfree(port);
+ return err;
+ }
+
+ adi_init_digital(gameport);
+ adi_read_packet(port);
+
+ if (port->adi[0].ret >= ADI_MIN_LEN_LENGTH)
+ adi_move_bits(port, adi_get_bits(port->adi, 10));
+
+ for (i = 0; i < 2; i++) {
+ adi_id_decode(port->adi + i, port);
+ adi_init_input(port->adi + i, port, i);
+ }
+
+ if (!port->adi[0].length && !port->adi[1].length) {
+ gameport_close(gameport);
+ kfree(port);
+ return -ENODEV;
+ }
+
+ gameport_set_poll_handler(gameport, adi_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ msleep(ADI_INIT_DELAY);
+ if (adi_read(port)) {
+ msleep(ADI_DATA_DELAY);
+ adi_read(port);
+ }
+
+ for (i = 0; i < 2; i++)
+ if (port->adi[i].length > 0) {
+ adi_init_center(port->adi + i);
+ input_register_device(&port->adi[i].dev);
+ printk(KERN_INFO "input: %s [%s] on %s\n",
+ port->adi[i].name, port->adi[i].cname, gameport->phys);
+ }
+
+ return 0;
+}
+
+static void adi_disconnect(struct gameport *gameport)
+{
+ int i;
+ struct adi_port *port = gameport_get_drvdata(gameport);
+
+ for (i = 0; i < 2; i++)
+ if (port->adi[i].length > 0)
+ input_unregister_device(&port->adi[i].dev);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(port);
+}
+
+/*
+ * The gameport device structure.
+ */
+
+static struct gameport_driver adi_drv = {
+ .driver = {
+ .name = "adi",
+ },
+ .description = DRIVER_DESC,
+ .connect = adi_connect,
+ .disconnect = adi_disconnect,
+};
+
+static int __init adi_init(void)
+{
+ gameport_register_driver(&adi_drv);
+ return 0;
+}
+
+static void __exit adi_exit(void)
+{
+ gameport_unregister_driver(&adi_drv);
+}
+
+module_init(adi_init);
+module_exit(adi_exit);
diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c
new file mode 100644
index 000000000000..cf36ca9b92f3
--- /dev/null
+++ b/drivers/input/joystick/amijoy.c
@@ -0,0 +1,161 @@
+/*
+ * $Id: amijoy.c,v 1.13 2002/01/22 20:26:32 vojtech Exp $
+ *
+ * Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Driver for Amiga joysticks 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/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/system.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Driver for Amiga joysticks");
+MODULE_LICENSE("GPL");
+
+static int amijoy[2] = { 0, 1 };
+module_param_array_named(map, amijoy, uint, NULL, 0);
+MODULE_PARM_DESC(map, "Map of attached joysticks in form of <a>,<b> (default is 0,1)");
+
+__obsolete_setup("amijoy=");
+
+static int amijoy_used[2] = { 0, 0 };
+static struct input_dev amijoy_dev[2];
+static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" };
+
+static char *amijoy_name = "Amiga joystick";
+
+static irqreturn_t amijoy_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+ int i, data = 0, button = 0;
+
+ for (i = 0; i < 2; i++)
+ if (amijoy[i]) {
+
+ switch (i) {
+ case 0: data = ~custom.joy0dat; button = (~ciaa.pra >> 6) & 1; break;
+ case 1: data = ~custom.joy1dat; button = (~ciaa.pra >> 7) & 1; break;
+ }
+
+ input_regs(amijoy_dev + i, fp);
+
+ input_report_key(amijoy_dev + i, BTN_TRIGGER, button);
+
+ input_report_abs(amijoy_dev + i, ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1));
+ data = ~(data ^ (data << 1));
+ input_report_abs(amijoy_dev + i, ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1));
+
+ input_sync(amijoy_dev + i);
+ }
+ return IRQ_HANDLED;
+}
+
+static int amijoy_open(struct input_dev *dev)
+{
+ int *used = dev->private;
+
+ if ((*used)++)
+ return 0;
+
+ if (request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) {
+ (*used)--;
+ printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void amijoy_close(struct input_dev *dev)
+{
+ int *used = dev->private;
+
+ if (!--(*used))
+ free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt);
+}
+
+static int __init amijoy_init(void)
+{
+ int i, j;
+
+ for (i = 0; i < 2; i++)
+ if (amijoy[i]) {
+ if (!request_mem_region(CUSTOM_PHYSADDR+10+i*2, 2,
+ "amijoy [Denise]")) {
+ if (i == 1 && amijoy[0]) {
+ input_unregister_device(amijoy_dev);
+ release_mem_region(CUSTOM_PHYSADDR+10, 2);
+ }
+ return -EBUSY;
+ }
+
+ amijoy_dev[i].open = amijoy_open;
+ amijoy_dev[i].close = amijoy_close;
+ amijoy_dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ amijoy_dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+ amijoy_dev[i].keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+ for (j = 0; j < 2; j++) {
+ amijoy_dev[i].absmin[ABS_X + j] = -1;
+ amijoy_dev[i].absmax[ABS_X + j] = 1;
+ }
+
+ amijoy_dev[i].name = amijoy_name;
+ amijoy_dev[i].phys = amijoy_phys[i];
+ amijoy_dev[i].id.bustype = BUS_AMIGA;
+ amijoy_dev[i].id.vendor = 0x0001;
+ amijoy_dev[i].id.product = 0x0003;
+ amijoy_dev[i].id.version = 0x0100;
+
+ amijoy_dev[i].private = amijoy_used + i;
+
+ input_register_device(amijoy_dev + i);
+ printk(KERN_INFO "input: %s at joy%ddat\n", amijoy_name, i);
+ }
+ return 0;
+}
+
+static void __exit amijoy_exit(void)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (amijoy[i]) {
+ input_unregister_device(amijoy_dev + i);
+ release_mem_region(CUSTOM_PHYSADDR+10+i*2, 2);
+ }
+}
+
+module_init(amijoy_init);
+module_exit(amijoy_exit);
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
new file mode 100644
index 000000000000..504b7d550567
--- /dev/null
+++ b/drivers/input/joystick/analog.c
@@ -0,0 +1,772 @@
+/*
+ * $Id: analog.c,v 1.68 2002/01/22 20:18:32 vojtech Exp $
+ *
+ * Copyright (c) 1996-2001 Vojtech Pavlik
+ */
+
+/*
+ * Analog joystick and gamepad 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/config.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <asm/timex.h>
+
+#define DRIVER_DESC "Analog joystick and gamepad driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Option parsing.
+ */
+
+#define ANALOG_PORTS 16
+
+static char *js[ANALOG_PORTS];
+static int js_nargs;
+static int analog_options[ANALOG_PORTS];
+module_param_array_named(map, js, charp, &js_nargs, 0);
+MODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities");
+
+__obsolete_setup("js=");
+
+/*
+ * Times, feature definitions.
+ */
+
+#define ANALOG_RUDDER 0x00004
+#define ANALOG_THROTTLE 0x00008
+#define ANALOG_AXES_STD 0x0000f
+#define ANALOG_BTNS_STD 0x000f0
+
+#define ANALOG_BTNS_CHF 0x00100
+#define ANALOG_HAT1_CHF 0x00200
+#define ANALOG_HAT2_CHF 0x00400
+#define ANALOG_HAT_FCS 0x00800
+#define ANALOG_HATS_ALL 0x00e00
+#define ANALOG_BTN_TL 0x01000
+#define ANALOG_BTN_TR 0x02000
+#define ANALOG_BTN_TL2 0x04000
+#define ANALOG_BTN_TR2 0x08000
+#define ANALOG_BTNS_TLR 0x03000
+#define ANALOG_BTNS_TLR2 0x0c000
+#define ANALOG_BTNS_GAMEPAD 0x0f000
+
+#define ANALOG_HBTN_CHF 0x10000
+#define ANALOG_ANY_CHF 0x10700
+#define ANALOG_SAITEK 0x20000
+#define ANALOG_EXTENSIONS 0x7ff00
+#define ANALOG_GAMEPAD 0x80000
+
+#define ANALOG_MAX_TIME 3 /* 3 ms */
+#define ANALOG_LOOP_TIME 2000 /* 2 * loop */
+#define ANALOG_SAITEK_DELAY 200 /* 200 us */
+#define ANALOG_SAITEK_TIME 2000 /* 2000 us */
+#define ANALOG_AXIS_TIME 2 /* 2 * refresh */
+#define ANALOG_INIT_RETRIES 8 /* 8 times */
+#define ANALOG_FUZZ_BITS 2 /* 2 bit more */
+#define ANALOG_FUZZ_MAGIC 36 /* 36 u*ms/loop */
+
+#define ANALOG_MAX_NAME_LENGTH 128
+#define ANALOG_MAX_PHYS_LENGTH 32
+
+static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE };
+static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+static short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR };
+static short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS };
+static short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE };
+static short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2,
+ BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 };
+
+static unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 };
+
+struct analog {
+ struct input_dev dev;
+ int mask;
+ short *buttons;
+ char name[ANALOG_MAX_NAME_LENGTH];
+ char phys[ANALOG_MAX_PHYS_LENGTH];
+};
+
+struct analog_port {
+ struct gameport *gameport;
+ struct analog analog[2];
+ unsigned char mask;
+ char saitek;
+ char cooked;
+ int bads;
+ int reads;
+ int speed;
+ int loop;
+ int fuzz;
+ int axes[4];
+ int buttons;
+ int initial[4];
+ int axtime;
+};
+
+/*
+ * Time macros.
+ */
+
+#ifdef __i386__
+#define GET_TIME(x) do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0)
+#define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? CLOCK_TICK_RATE / HZ : 0)))
+#define TIME_NAME (cpu_has_tsc?"TSC":"PIT")
+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;
+}
+#elif defined(__x86_64__)
+#define GET_TIME(x) rdtscl(x)
+#define DELTA(x,y) ((y)-(x))
+#define TIME_NAME "TSC"
+#elif defined(__alpha__)
+#define GET_TIME(x) do { x = get_cycles(); } while (0)
+#define DELTA(x,y) ((y)-(x))
+#define TIME_NAME "PCC"
+#else
+#define FAKE_TIME
+static unsigned long analog_faketime = 0;
+#define GET_TIME(x) do { x = analog_faketime++; } while(0)
+#define DELTA(x,y) ((y)-(x))
+#define TIME_NAME "Unreliable"
+#warning Precise timer not defined for this architecture.
+#endif
+
+/*
+ * analog_decode() decodes analog joystick data and reports input events.
+ */
+
+static void analog_decode(struct analog *analog, int *axes, int *initial, int buttons)
+{
+ struct input_dev *dev = &analog->dev;
+ int i, j;
+
+ if (analog->mask & ANALOG_HAT_FCS)
+ for (i = 0; i < 4; i++)
+ if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) {
+ buttons |= 1 << (i + 14);
+ break;
+ }
+
+ for (i = j = 0; i < 6; i++)
+ if (analog->mask & (0x10 << i))
+ input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1);
+
+ if (analog->mask & ANALOG_HBTN_CHF)
+ for (i = 0; i < 4; i++)
+ input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1);
+
+ if (analog->mask & ANALOG_BTN_TL)
+ input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1));
+ if (analog->mask & ANALOG_BTN_TR)
+ input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1));
+ if (analog->mask & ANALOG_BTN_TL2)
+ input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1)));
+ if (analog->mask & ANALOG_BTN_TR2)
+ input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1)));
+
+ for (i = j = 0; i < 4; i++)
+ if (analog->mask & (1 << i))
+ input_report_abs(dev, analog_axes[j++], axes[i]);
+
+ for (i = j = 0; i < 3; i++)
+ if (analog->mask & analog_exts[i]) {
+ input_report_abs(dev, analog_hats[j++],
+ ((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1));
+ input_report_abs(dev, analog_hats[j++],
+ ((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1));
+ }
+
+ input_sync(dev);
+}
+
+/*
+ * analog_cooked_read() reads analog joystick data.
+ */
+
+static int analog_cooked_read(struct analog_port *port)
+{
+ struct gameport *gameport = port->gameport;
+ unsigned int time[4], start, loop, now, loopout, timeout;
+ unsigned char data[4], this, last;
+ unsigned long flags;
+ int i, j;
+
+ loopout = (ANALOG_LOOP_TIME * port->loop) / 1000;
+ timeout = ANALOG_MAX_TIME * port->speed;
+
+ local_irq_save(flags);
+ gameport_trigger(gameport);
+ GET_TIME(now);
+ local_irq_restore(flags);
+
+ start = now;
+ this = port->mask;
+ i = 0;
+
+ do {
+ loop = now;
+ last = this;
+
+ local_irq_disable();
+ this = gameport_read(gameport) & port->mask;
+ GET_TIME(now);
+ local_irq_restore(flags);
+
+ if ((last ^ this) && (DELTA(loop, now) < loopout)) {
+ data[i] = last ^ this;
+ time[i] = now;
+ i++;
+ }
+
+ } while (this && (i < 4) && (DELTA(start, now) < timeout));
+
+ this <<= 4;
+
+ for (--i; i >= 0; i--) {
+ this |= data[i];
+ for (j = 0; j < 4; j++)
+ if (data[i] & (1 << j))
+ port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
+ }
+
+ return -(this != port->mask);
+}
+
+static int analog_button_read(struct analog_port *port, char saitek, char chf)
+{
+ unsigned char u;
+ int t = 1, i = 0;
+ int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME);
+
+ u = gameport_read(port->gameport);
+
+ if (!chf) {
+ port->buttons = (~u >> 4) & 0xf;
+ return 0;
+ }
+
+ port->buttons = 0;
+
+ while ((~u & 0xf0) && (i < 16) && t) {
+ port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf];
+ if (!saitek) return 0;
+ udelay(ANALOG_SAITEK_DELAY);
+ t = strobe;
+ gameport_trigger(port->gameport);
+ while (((u = gameport_read(port->gameport)) & port->mask) && t) t--;
+ i++;
+ }
+
+ return -(!t || (i == 16));
+}
+
+/*
+ * analog_poll() repeatedly polls the Analog joysticks.
+ */
+
+static void analog_poll(struct gameport *gameport)
+{
+ struct analog_port *port = gameport_get_drvdata(gameport);
+ int i;
+
+ char saitek = !!(port->analog[0].mask & ANALOG_SAITEK);
+ char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF);
+
+ if (port->cooked) {
+ port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons);
+ if (chf)
+ port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0;
+ port->reads++;
+ } else {
+ if (!port->axtime--) {
+ port->bads -= analog_cooked_read(port);
+ port->bads -= analog_button_read(port, saitek, chf);
+ port->reads++;
+ port->axtime = ANALOG_AXIS_TIME - 1;
+ } else {
+ if (!saitek)
+ analog_button_read(port, saitek, chf);
+ }
+ }
+
+ for (i = 0; i < 2; i++)
+ if (port->analog[i].mask)
+ analog_decode(port->analog + i, port->axes, port->initial, port->buttons);
+}
+
+/*
+ * analog_open() is a callback from the input open routine.
+ */
+
+static int analog_open(struct input_dev *dev)
+{
+ struct analog_port *port = dev->private;
+
+ gameport_start_polling(port->gameport);
+ return 0;
+}
+
+/*
+ * analog_close() is a callback from the input close routine.
+ */
+
+static void analog_close(struct input_dev *dev)
+{
+ struct analog_port *port = dev->private;
+
+ gameport_stop_polling(port->gameport);
+}
+
+/*
+ * analog_calibrate_timer() calibrates the timer and computes loop
+ * and timeout values for a joystick port.
+ */
+
+static void analog_calibrate_timer(struct analog_port *port)
+{
+ struct gameport *gameport = port->gameport;
+ unsigned int i, t, tx, t1, t2, t3;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ GET_TIME(t1);
+#ifdef FAKE_TIME
+ analog_faketime += 830;
+#endif
+ mdelay(1);
+ GET_TIME(t2);
+ GET_TIME(t3);
+ local_irq_restore(flags);
+
+ port->speed = DELTA(t1, t2) - DELTA(t2, t3);
+
+ tx = ~0;
+
+ for (i = 0; i < 50; i++) {
+ local_irq_save(flags);
+ GET_TIME(t1);
+ for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); }
+ GET_TIME(t3);
+ local_irq_restore(flags);
+ udelay(i);
+ t = DELTA(t1, t2) - DELTA(t2, t3);
+ if (t < tx) tx = t;
+ }
+
+ port->loop = tx / 50;
+}
+
+/*
+ * analog_name() constructs a name for an analog joystick.
+ */
+
+static void analog_name(struct analog *analog)
+{
+ sprintf(analog->name, "Analog %d-axis %d-button",
+ hweight8(analog->mask & ANALOG_AXES_STD),
+ hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
+ hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
+
+ if (analog->mask & ANALOG_HATS_ALL)
+ sprintf(analog->name, "%s %d-hat",
+ analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
+
+ if (analog->mask & ANALOG_HAT_FCS)
+ strcat(analog->name, " FCS");
+ if (analog->mask & ANALOG_ANY_CHF)
+ strcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
+
+ strcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick");
+}
+
+/*
+ * analog_init_device()
+ */
+
+static void analog_init_device(struct analog_port *port, struct analog *analog, int index)
+{
+ int i, j, t, v, w, x, y, z;
+
+ analog_name(analog);
+ sprintf(analog->phys, "%s/input%d", port->gameport->phys, index);
+ analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn;
+
+ init_input_dev(&analog->dev);
+
+ analog->dev.name = analog->name;
+ analog->dev.phys = analog->phys;
+ analog->dev.id.bustype = BUS_GAMEPORT;
+ analog->dev.id.vendor = GAMEPORT_ID_VENDOR_ANALOG;
+ analog->dev.id.product = analog->mask >> 4;
+ analog->dev.id.version = 0x0100;
+
+ analog->dev.open = analog_open;
+ analog->dev.close = analog_close;
+ analog->dev.private = port;
+ analog->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (i = j = 0; i < 4; i++)
+ if (analog->mask & (1 << i)) {
+
+ t = analog_axes[j];
+ x = port->axes[i];
+ y = (port->axes[0] + port->axes[1]) >> 1;
+ z = y - port->axes[i];
+ z = z > 0 ? z : -z;
+ v = (x >> 3);
+ w = (x >> 3);
+
+ set_bit(t, analog->dev.absbit);
+
+ if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3)))
+ x = y;
+
+ if (analog->mask & ANALOG_SAITEK) {
+ if (i == 2) x = port->axes[i];
+ v = x - (x >> 2);
+ w = (x >> 4);
+ }
+
+ analog->dev.absmax[t] = (x << 1) - v;
+ analog->dev.absmin[t] = v;
+ analog->dev.absfuzz[t] = port->fuzz;
+ analog->dev.absflat[t] = w;
+
+ j++;
+ }
+
+ for (i = j = 0; i < 3; i++)
+ if (analog->mask & analog_exts[i])
+ for (x = 0; x < 2; x++) {
+ t = analog_hats[j++];
+ set_bit(t, analog->dev.absbit);
+ analog->dev.absmax[t] = 1;
+ analog->dev.absmin[t] = -1;
+ }
+
+ for (i = j = 0; i < 4; i++)
+ if (analog->mask & (0x10 << i))
+ set_bit(analog->buttons[j++], analog->dev.keybit);
+
+ if (analog->mask & ANALOG_BTNS_CHF)
+ for (i = 0; i < 2; i++)
+ set_bit(analog->buttons[j++], analog->dev.keybit);
+
+ if (analog->mask & ANALOG_HBTN_CHF)
+ for (i = 0; i < 4; i++)
+ set_bit(analog->buttons[j++], analog->dev.keybit);
+
+ for (i = 0; i < 4; i++)
+ if (analog->mask & (ANALOG_BTN_TL << i))
+ set_bit(analog_pads[i], analog->dev.keybit);
+
+ analog_decode(analog, port->axes, port->initial, port->buttons);
+
+ input_register_device(&analog->dev);
+
+ printk(KERN_INFO "input: %s at %s", analog->name, port->gameport->phys);
+
+ if (port->cooked)
+ printk(" [ADC port]\n");
+ else
+ printk(" [%s timer, %d %sHz clock, %d ns res]\n", TIME_NAME,
+ port->speed > 10000 ? (port->speed + 800) / 1000 : port->speed,
+ port->speed > 10000 ? "M" : "k",
+ port->speed > 10000 ? (port->loop * 1000) / (port->speed / 1000)
+ : (port->loop * 1000000) / port->speed);
+}
+
+/*
+ * analog_init_devices() sets up device-specific values and registers the input devices.
+ */
+
+static int analog_init_masks(struct analog_port *port)
+{
+ int i;
+ struct analog *analog = port->analog;
+ int max[4];
+
+ if (!port->mask)
+ return -1;
+
+ if ((port->mask & 3) != 3 && port->mask != 0xc) {
+ printk(KERN_WARNING "analog.c: Unknown joystick device found "
+ "(data=%#x, %s), probably not analog joystick.\n",
+ port->mask, port->gameport->phys);
+ return -1;
+ }
+
+
+ i = analog_options[0]; /* FIXME !!! - need to specify options for different ports */
+
+ analog[0].mask = i & 0xfffff;
+
+ analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD)
+ | port->mask | ((port->mask << 8) & ANALOG_HAT_FCS)
+ | ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2);
+
+ analog[0].mask &= ~(ANALOG_HAT2_CHF)
+ | ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF);
+
+ analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2)
+ | ((~analog[0].mask & ANALOG_HAT_FCS) >> 8)
+ | ((~analog[0].mask & ANALOG_HAT_FCS) << 2)
+ | ((~analog[0].mask & ANALOG_HAT_FCS) << 4);
+
+ analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER)
+ | (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10)
+ & ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12));
+
+ analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000);
+
+ analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD
+ : (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD);
+
+ if (port->cooked) {
+
+ for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1;
+
+ if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1;
+ if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1;
+ if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1;
+ if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1;
+ if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1;
+
+ gameport_calibrate(port->gameport, port->axes, max);
+ }
+
+ for (i = 0; i < 4; i++)
+ port->initial[i] = port->axes[i];
+
+ return -!(analog[0].mask || analog[1].mask);
+}
+
+static int analog_init_port(struct gameport *gameport, struct gameport_driver *drv, struct analog_port *port)
+{
+ int i, t, u, v;
+
+ port->gameport = gameport;
+
+ gameport_set_drvdata(gameport, port);
+
+ if (!gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) {
+
+ analog_calibrate_timer(port);
+
+ gameport_trigger(gameport);
+ t = gameport_read(gameport);
+ msleep(ANALOG_MAX_TIME);
+ port->mask = (gameport_read(gameport) ^ t) & t & 0xf;
+ port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS;
+
+ for (i = 0; i < ANALOG_INIT_RETRIES; i++) {
+ if (!analog_cooked_read(port))
+ break;
+ msleep(ANALOG_MAX_TIME);
+ }
+
+ u = v = 0;
+
+ msleep(ANALOG_MAX_TIME);
+ t = gameport_time(gameport, ANALOG_MAX_TIME * 1000);
+ gameport_trigger(gameport);
+ while ((gameport_read(port->gameport) & port->mask) && (u < t))
+ u++;
+ udelay(ANALOG_SAITEK_DELAY);
+ t = gameport_time(gameport, ANALOG_SAITEK_TIME);
+ gameport_trigger(gameport);
+ while ((gameport_read(port->gameport) & port->mask) && (v < t))
+ v++;
+
+ if (v < (u >> 1)) { /* FIXME - more than one port */
+ analog_options[0] |= /* FIXME - more than one port */
+ ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF;
+ return 0;
+ }
+
+ gameport_close(gameport);
+ }
+
+ if (!gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) {
+
+ for (i = 0; i < ANALOG_INIT_RETRIES; i++)
+ if (!gameport_cooked_read(gameport, port->axes, &port->buttons))
+ break;
+ for (i = 0; i < 4; i++)
+ if (port->axes[i] != -1)
+ port->mask |= 1 << i;
+
+ port->fuzz = gameport->fuzz;
+ port->cooked = 1;
+ return 0;
+ }
+
+ return gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+}
+
+static int analog_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct analog_port *port;
+ int i;
+ int err;
+
+ if (!(port = kcalloc(1, sizeof(struct analog_port), GFP_KERNEL)))
+ return - ENOMEM;
+
+ err = analog_init_port(gameport, drv, port);
+ if (err) {
+ kfree(port);
+ return err;
+ }
+
+ err = analog_init_masks(port);
+ if (err) {
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(port);
+ return err;
+ }
+
+ gameport_set_poll_handler(gameport, analog_poll);
+ gameport_set_poll_interval(gameport, 10);
+
+ for (i = 0; i < 2; i++)
+ if (port->analog[i].mask)
+ analog_init_device(port, port->analog + i, i);
+
+ return 0;
+}
+
+static void analog_disconnect(struct gameport *gameport)
+{
+ int i;
+ struct analog_port *port = gameport_get_drvdata(gameport);
+
+ for (i = 0; i < 2; i++)
+ if (port->analog[i].mask)
+ input_unregister_device(&port->analog[i].dev);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on %s failed\n",
+ port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0,
+ port->gameport->phys);
+ kfree(port);
+}
+
+struct analog_types {
+ char *name;
+ int value;
+};
+
+static struct analog_types analog_types[] = {
+ { "none", 0x00000000 },
+ { "auto", 0x000000ff },
+ { "2btn", 0x0000003f },
+ { "y-joy", 0x0cc00033 },
+ { "y-pad", 0x8cc80033 },
+ { "fcs", 0x000008f7 },
+ { "chf", 0x000002ff },
+ { "fullchf", 0x000007ff },
+ { "gamepad", 0x000830f3 },
+ { "gamepad8", 0x0008f0f3 },
+ { NULL, 0 }
+};
+
+static void analog_parse_options(void)
+{
+ int i, j;
+ char *end;
+
+ for (i = 0; i < js_nargs; i++) {
+
+ for (j = 0; analog_types[j].name; j++)
+ if (!strcmp(analog_types[j].name, js[i])) {
+ analog_options[i] = analog_types[j].value;
+ break;
+ }
+ if (analog_types[j].name) continue;
+
+ analog_options[i] = simple_strtoul(js[i], &end, 0);
+ if (end != js[i]) continue;
+
+ analog_options[i] = 0xff;
+ if (!strlen(js[i])) continue;
+
+ printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]);
+ }
+
+ for (; i < ANALOG_PORTS; i++)
+ analog_options[i] = 0xff;
+}
+
+/*
+ * The gameport device structure.
+ */
+
+static struct gameport_driver analog_drv = {
+ .driver = {
+ .name = "analog",
+ },
+ .description = DRIVER_DESC,
+ .connect = analog_connect,
+ .disconnect = analog_disconnect,
+};
+
+static int __init analog_init(void)
+{
+ analog_parse_options();
+ gameport_register_driver(&analog_drv);
+
+ return 0;
+}
+
+static void __exit analog_exit(void)
+{
+ gameport_unregister_driver(&analog_drv);
+}
+
+module_init(analog_init);
+module_exit(analog_exit);
diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c
new file mode 100644
index 000000000000..a6002205328f
--- /dev/null
+++ b/drivers/input/joystick/cobra.c
@@ -0,0 +1,264 @@
+/*
+ * $Id: cobra.c,v 1.19 2002/01/22 20:26:52 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Creative Labs Blaster GamePad Cobra 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/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC "Creative Labs Blaster GamePad Cobra driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */
+#define COBRA_LENGTH 36
+
+static char* cobra_name = "Creative Labs Blaster GamePad Cobra";
+
+static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 };
+
+struct cobra {
+ struct gameport *gameport;
+ struct input_dev dev[2];
+ int reads;
+ int bads;
+ unsigned char exists;
+ char phys[2][32];
+};
+
+static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data)
+{
+ unsigned long flags;
+ unsigned char u, v, w;
+ __u64 buf[2];
+ int r[2], t[2];
+ int i, j, ret;
+
+ int strobe = gameport_time(gameport, COBRA_MAX_STROBE);
+
+ for (i = 0; i < 2; i++) {
+ r[i] = buf[i] = 0;
+ t[i] = COBRA_MAX_STROBE;
+ }
+
+ local_irq_save(flags);
+
+ u = gameport_read(gameport);
+
+ do {
+ t[0]--; t[1]--;
+ v = gameport_read(gameport);
+ for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2)
+ if (w & 0x30) {
+ if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) {
+ buf[i] |= (__u64)((w >> 5) & 1) << r[i]++;
+ t[i] = strobe;
+ u = v;
+ } else t[i] = 0;
+ }
+ } while (t[0] > 0 || t[1] > 0);
+
+ local_irq_restore(flags);
+
+ ret = 0;
+
+ for (i = 0; i < 2; i++) {
+
+ if (r[i] != COBRA_LENGTH) continue;
+
+ for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++)
+ buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1));
+
+ if (j < COBRA_LENGTH) ret |= (1 << i);
+
+ data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0)
+ | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000)
+ | ((buf[i] >> 11) & 0x1f00000);
+
+ }
+
+ return ret;
+}
+
+static void cobra_poll(struct gameport *gameport)
+{
+ struct cobra *cobra = gameport_get_drvdata(gameport);
+ struct input_dev *dev;
+ unsigned int data[2];
+ int i, j, r;
+
+ cobra->reads++;
+
+ if ((r = cobra_read_packet(gameport, data)) != cobra->exists) {
+ cobra->bads++;
+ return;
+ }
+
+ for (i = 0; i < 2; i++)
+ if (cobra->exists & r & (1 << i)) {
+
+ dev = cobra->dev + i;
+
+ input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1));
+ input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1));
+
+ for (j = 0; cobra_btn[j]; j++)
+ input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j));
+
+ input_sync(dev);
+
+ }
+}
+
+static int cobra_open(struct input_dev *dev)
+{
+ struct cobra *cobra = dev->private;
+
+ gameport_start_polling(cobra->gameport);
+ return 0;
+}
+
+static void cobra_close(struct input_dev *dev)
+{
+ struct cobra *cobra = dev->private;
+
+ gameport_stop_polling(cobra->gameport);
+}
+
+static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct cobra *cobra;
+ unsigned int data[2];
+ int i, j;
+ int err;
+
+ if (!(cobra = kcalloc(1, sizeof(struct cobra), GFP_KERNEL)))
+ return -ENOMEM;
+
+ cobra->gameport = gameport;
+
+ gameport_set_drvdata(gameport, cobra);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ cobra->exists = cobra_read_packet(gameport, data);
+
+ for (i = 0; i < 2; i++)
+ if ((cobra->exists >> i) & data[i] & 1) {
+ printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d"
+ " Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7);
+ cobra->exists &= ~(1 << i);
+ }
+
+ if (!cobra->exists) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ gameport_set_poll_handler(gameport, cobra_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ for (i = 0; i < 2; i++)
+ if ((cobra->exists >> i) & 1) {
+
+ sprintf(cobra->phys[i], "%s/input%d", gameport->phys, i);
+
+ cobra->dev[i].private = cobra;
+ cobra->dev[i].open = cobra_open;
+ cobra->dev[i].close = cobra_close;
+
+ cobra->dev[i].name = cobra_name;
+ cobra->dev[i].phys = cobra->phys[i];
+ cobra->dev[i].id.bustype = BUS_GAMEPORT;
+ cobra->dev[i].id.vendor = GAMEPORT_ID_VENDOR_CREATIVE;
+ cobra->dev[i].id.product = 0x0008;
+ cobra->dev[i].id.version = 0x0100;
+
+ cobra->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ input_set_abs_params(&cobra->dev[i], ABS_X, -1, 1, 0, 0);
+ input_set_abs_params(&cobra->dev[i], ABS_Y, -1, 1, 0, 0);
+
+ for (j = 0; cobra_btn[j]; j++)
+ set_bit(cobra_btn[j], cobra->dev[i].keybit);
+
+ input_register_device(&cobra->dev[i]);
+ printk(KERN_INFO "input: %s on %s\n", cobra_name, gameport->phys);
+ }
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(cobra);
+ return err;
+}
+
+static void cobra_disconnect(struct gameport *gameport)
+{
+ struct cobra *cobra = gameport_get_drvdata(gameport);
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if ((cobra->exists >> i) & 1)
+ input_unregister_device(cobra->dev + i);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(cobra);
+}
+
+static struct gameport_driver cobra_drv = {
+ .driver = {
+ .name = "cobra",
+ },
+ .description = DRIVER_DESC,
+ .connect = cobra_connect,
+ .disconnect = cobra_disconnect,
+};
+
+static int __init cobra_init(void)
+{
+ gameport_register_driver(&cobra_drv);
+ return 0;
+}
+
+static void __exit cobra_exit(void)
+{
+ gameport_unregister_driver(&cobra_drv);
+}
+
+module_init(cobra_init);
+module_exit(cobra_exit);
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
new file mode 100644
index 000000000000..cfdd3acf06a1
--- /dev/null
+++ b/drivers/input/joystick/db9.c
@@ -0,0 +1,647 @@
+/*
+ * $Id: db9.c,v 1.13 2002/04/07 20:13:37 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Andree Borrmann Mats Sjövall
+ */
+
+/*
+ * Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver");
+MODULE_LICENSE("GPL");
+
+static int db9[] __initdata = { -1, 0 };
+static int db9_nargs __initdata = 0;
+module_param_array_named(dev, db9, int, &db9_nargs, 0);
+MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)");
+
+static int db9_2[] __initdata = { -1, 0 };
+static int db9_nargs_2 __initdata = 0;
+module_param_array_named(dev2, db9_2, int, &db9_nargs_2, 0);
+MODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)");
+
+static int db9_3[] __initdata = { -1, 0 };
+static int db9_nargs_3 __initdata = 0;
+module_param_array_named(dev3, db9_3, int, &db9_nargs_3, 0);
+MODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)");
+
+__obsolete_setup("db9=");
+__obsolete_setup("db9_2=");
+__obsolete_setup("db9_3=");
+
+#define DB9_MULTI_STICK 0x01
+#define DB9_MULTI2_STICK 0x02
+#define DB9_GENESIS_PAD 0x03
+#define DB9_GENESIS5_PAD 0x05
+#define DB9_GENESIS6_PAD 0x06
+#define DB9_SATURN_PAD 0x07
+#define DB9_MULTI_0802 0x08
+#define DB9_MULTI_0802_2 0x09
+#define DB9_CD32_PAD 0x0A
+#define DB9_SATURN_DPP 0x0B
+#define DB9_SATURN_DPP_2 0x0C
+#define DB9_MAX_PAD 0x0D
+
+#define DB9_UP 0x01
+#define DB9_DOWN 0x02
+#define DB9_LEFT 0x04
+#define DB9_RIGHT 0x08
+#define DB9_FIRE1 0x10
+#define DB9_FIRE2 0x20
+#define DB9_FIRE3 0x40
+#define DB9_FIRE4 0x80
+
+#define DB9_NORMAL 0x0a
+#define DB9_NOSELECT 0x08
+
+#define DB9_MAX_DEVICES 2
+
+#define DB9_GENESIS6_DELAY 14
+#define DB9_REFRESH_TIME HZ/100
+
+struct db9 {
+ struct input_dev dev[DB9_MAX_DEVICES];
+ struct timer_list timer;
+ struct pardevice *pd;
+ int mode;
+ int used;
+ char phys[2][32];
+};
+
+static struct db9 *db9_base[3];
+
+static short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB };
+static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
+static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
+
+static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 9, 1, 1, 7, 9, 9 };
+static short *db9_btn[DB9_MAX_PAD] = { NULL, db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn,
+ db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn,
+ db9_cd32_btn, db9_cd32_btn };
+static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad",
+ NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick",
+ "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad", "Saturn dpp", "Saturn dpp dual" };
+
+static const int db9_max_pads[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 6, 1, 2, 1, 6, 12 };
+static const int db9_num_axis[DB9_MAX_PAD] = { 0, 2, 2, 2, 0, 2, 2, 7, 2, 2, 2 ,7, 7 };
+static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+static const int db9_bidirectional[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0 };
+static const int db9_reverse[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 };
+
+/*
+ * Saturn controllers
+ */
+#define DB9_SATURN_DELAY 300
+static const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 };
+static const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 };
+
+/*
+ * db9_saturn_write_sub() writes 2 bit data.
+ */
+static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub)
+{
+ unsigned char c;
+
+ switch (type) {
+ case 1: /* DPP1 */
+ c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data;
+ parport_write_data(port, c);
+ break;
+ case 2: /* DPP2 */
+ c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03;
+ parport_write_data(port, c);
+ break;
+ case 0: /* DB9 */
+ c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered;
+ parport_write_control(port, c);
+ break;
+ }
+}
+
+/*
+ * gc_saturn_read_sub() reads 4 bit data.
+ */
+static unsigned char db9_saturn_read_sub(struct parport *port, int type)
+{
+ unsigned char data;
+
+ if (type) {
+ /* DPP */
+ data = parport_read_status(port) ^ 0x80;
+ return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0)
+ | (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0);
+ } else {
+ /* DB9 */
+ data = parport_read_data(port) & 0x0f;
+ return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0)
+ | (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0);
+ }
+}
+
+/*
+ * db9_saturn_read_analog() sends clock and reads 8 bit data.
+ */
+static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered)
+{
+ unsigned char data;
+
+ db9_saturn_write_sub(port, type, 0, powered, 0);
+ udelay(DB9_SATURN_DELAY);
+ data = db9_saturn_read_sub(port, type) << 4;
+ db9_saturn_write_sub(port, type, 2, powered, 0);
+ udelay(DB9_SATURN_DELAY);
+ data |= db9_saturn_read_sub(port, type);
+ return data;
+}
+
+/*
+ * db9_saturn_read_packet() reads whole saturn packet at connector
+ * and returns device identifier code.
+ */
+static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered)
+{
+ int i, j;
+ unsigned char tmp;
+
+ db9_saturn_write_sub(port, type, 3, powered, 0);
+ data[0] = db9_saturn_read_sub(port, type);
+ switch (data[0] & 0x0f) {
+ case 0xf:
+ /* 1111 no pad */
+ return data[0] = 0xff;
+ case 0x4: case 0x4 | 0x8:
+ /* ?100 : digital controller */
+ db9_saturn_write_sub(port, type, 0, powered, 1);
+ data[2] = db9_saturn_read_sub(port, type) << 4;
+ db9_saturn_write_sub(port, type, 2, powered, 1);
+ data[1] = db9_saturn_read_sub(port, type) << 4;
+ db9_saturn_write_sub(port, type, 1, powered, 1);
+ data[1] |= db9_saturn_read_sub(port, type);
+ db9_saturn_write_sub(port, type, 3, powered, 1);
+ /* data[2] |= db9_saturn_read_sub(port, type); */
+ data[2] |= data[0];
+ return data[0] = 0x02;
+ case 0x1:
+ /* 0001 : analog controller or multitap */
+ db9_saturn_write_sub(port, type, 2, powered, 0);
+ udelay(DB9_SATURN_DELAY);
+ data[0] = db9_saturn_read_analog(port, type, powered);
+ if (data[0] != 0x41) {
+ /* read analog controller */
+ for (i = 0; i < (data[0] & 0x0f); i++)
+ data[i + 1] = db9_saturn_read_analog(port, type, powered);
+ db9_saturn_write_sub(port, type, 3, powered, 0);
+ return data[0];
+ } else {
+ /* read multitap */
+ if (db9_saturn_read_analog(port, type, powered) != 0x60)
+ return data[0] = 0xff;
+ for (i = 0; i < 60; i += 10) {
+ data[i] = db9_saturn_read_analog(port, type, powered);
+ if (data[i] != 0xff)
+ /* read each pad */
+ for (j = 0; j < (data[i] & 0x0f); j++)
+ data[i + j + 1] = db9_saturn_read_analog(port, type, powered);
+ }
+ db9_saturn_write_sub(port, type, 3, powered, 0);
+ return 0x41;
+ }
+ case 0x0:
+ /* 0000 : mouse */
+ db9_saturn_write_sub(port, type, 2, powered, 0);
+ udelay(DB9_SATURN_DELAY);
+ tmp = db9_saturn_read_analog(port, type, powered);
+ if (tmp == 0xff) {
+ for (i = 0; i < 3; i++)
+ data[i + 1] = db9_saturn_read_analog(port, type, powered);
+ db9_saturn_write_sub(port, type, 3, powered, 0);
+ return data[0] = 0xe3;
+ }
+ default:
+ return data[0];
+ }
+}
+
+/*
+ * db9_saturn_report() analyzes packet and reports.
+ */
+static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *dev, int n, int max_pads)
+{
+ int tmp, i, j;
+
+ tmp = (id == 0x41) ? 60 : 10;
+ for (j = 0; (j < tmp) && (n < max_pads); j += 10, n++) {
+ switch (data[j]) {
+ case 0x16: /* multi controller (analog 4 axis) */
+ input_report_abs(dev + n, db9_abs[5], data[j + 6]);
+ case 0x15: /* mission stick (analog 3 axis) */
+ input_report_abs(dev + n, db9_abs[3], data[j + 4]);
+ input_report_abs(dev + n, db9_abs[4], data[j + 5]);
+ case 0x13: /* racing controller (analog 1 axis) */
+ input_report_abs(dev + n, db9_abs[2], data[j + 3]);
+ case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
+ case 0x02: /* digital pad (digital 2 axis + buttons) */
+ input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+ input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+ for (i = 0; i < 9; i++)
+ input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+ break;
+ case 0x19: /* mission stick x2 (analog 6 axis + buttons) */
+ input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+ input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+ for (i = 0; i < 9; i++)
+ input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+ input_report_abs(dev + n, db9_abs[2], data[j + 3]);
+ input_report_abs(dev + n, db9_abs[3], data[j + 4]);
+ input_report_abs(dev + n, db9_abs[4], data[j + 5]);
+ /*
+ input_report_abs(dev + n, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
+ input_report_abs(dev + n, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
+ */
+ input_report_abs(dev + n, db9_abs[6], data[j + 7]);
+ input_report_abs(dev + n, db9_abs[7], data[j + 8]);
+ input_report_abs(dev + n, db9_abs[5], data[j + 9]);
+ break;
+ case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */
+ input_report_key(dev + n, BTN_A, data[j + 3] & 0x80);
+ input_report_abs(dev + n, db9_abs[2], data[j + 3] & 0x7f);
+ break;
+ case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
+ input_report_key(dev + n, BTN_START, data[j + 1] & 0x08);
+ input_report_key(dev + n, BTN_A, data[j + 1] & 0x04);
+ input_report_key(dev + n, BTN_C, data[j + 1] & 0x02);
+ input_report_key(dev + n, BTN_B, data[j + 1] & 0x01);
+ input_report_abs(dev + n, db9_abs[2], data[j + 2] ^ 0x80);
+ input_report_abs(dev + n, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
+ break;
+ case 0xff:
+ default: /* no pad */
+ input_report_abs(dev + n, db9_abs[0], 0);
+ input_report_abs(dev + n, db9_abs[1], 0);
+ for (i = 0; i < 9; i++)
+ input_report_key(dev + n, db9_cd32_btn[i], 0);
+ break;
+ }
+ }
+ return n;
+}
+
+static int db9_saturn(int mode, struct parport *port, struct input_dev *dev)
+{
+ unsigned char id, data[60];
+ int type, n, max_pads;
+ int tmp, i;
+
+ switch (mode) {
+ case DB9_SATURN_PAD:
+ type = 0;
+ n = 1;
+ break;
+ case DB9_SATURN_DPP:
+ type = 1;
+ n = 1;
+ break;
+ case DB9_SATURN_DPP_2:
+ type = 1;
+ n = 2;
+ break;
+ default:
+ return -1;
+ }
+ max_pads = min(db9_max_pads[mode], DB9_MAX_DEVICES);
+ for (tmp = 0, i = 0; i < n; i++) {
+ id = db9_saturn_read_packet(port, data, type + i, 1);
+ tmp = db9_saturn_report(id, data, dev, tmp, max_pads);
+ }
+ return 0;
+}
+
+static void db9_timer(unsigned long private)
+{
+ struct db9 *db9 = (void *) private;
+ struct parport *port = db9->pd->port;
+ struct input_dev *dev = db9->dev;
+ int data, i;
+
+ switch(db9->mode) {
+ case DB9_MULTI_0802_2:
+
+ data = parport_read_data(port) >> 3;
+
+ input_report_abs(dev + 1, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev + 1, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+ input_report_key(dev + 1, BTN_TRIGGER, ~data & DB9_FIRE1);
+
+ case DB9_MULTI_0802:
+
+ data = parport_read_status(port) >> 3;
+
+ input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+ input_report_key(dev, BTN_TRIGGER, data & DB9_FIRE1);
+ break;
+
+ case DB9_MULTI_STICK:
+
+ data = parport_read_data(port);
+
+ input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+ input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
+ break;
+
+ case DB9_MULTI2_STICK:
+
+ data = parport_read_data(port);
+
+ input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+ input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
+ input_report_key(dev, BTN_THUMB, ~data & DB9_FIRE2);
+ break;
+
+ case DB9_GENESIS_PAD:
+
+ parport_write_control(port, DB9_NOSELECT);
+ data = parport_read_data(port);
+
+ input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+ input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+ input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+ parport_write_control(port, DB9_NORMAL);
+ data=parport_read_data(port);
+
+ input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
+ input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
+ break;
+
+ case DB9_GENESIS5_PAD:
+
+ parport_write_control(port, DB9_NOSELECT);
+ data=parport_read_data(port);
+
+ input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+ input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+ input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+ parport_write_control(port, DB9_NORMAL);
+ 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_Y, ~data & DB9_LEFT);
+ input_report_key(dev, BTN_START, ~data & DB9_RIGHT);
+ break;
+
+ case DB9_GENESIS6_PAD:
+
+ parport_write_control(port, DB9_NOSELECT); /* 1 */
+ udelay(DB9_GENESIS6_DELAY);
+ data=parport_read_data(port);
+
+ input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+ input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+ input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+ parport_write_control(port, DB9_NORMAL);
+ udelay(DB9_GENESIS6_DELAY);
+ data=parport_read_data(port);
+
+ input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
+ input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
+
+ parport_write_control(port, DB9_NOSELECT); /* 2 */
+ udelay(DB9_GENESIS6_DELAY);
+ parport_write_control(port, DB9_NORMAL);
+ udelay(DB9_GENESIS6_DELAY);
+ parport_write_control(port, DB9_NOSELECT); /* 3 */
+ udelay(DB9_GENESIS6_DELAY);
+ data=parport_read_data(port);
+
+ 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);
+ parport_write_control(port, DB9_NOSELECT); /* 4 */
+ udelay(DB9_GENESIS6_DELAY);
+ parport_write_control(port, DB9_NORMAL);
+ break;
+
+ case DB9_SATURN_PAD:
+ case DB9_SATURN_DPP:
+ case DB9_SATURN_DPP_2:
+
+ db9_saturn(db9->mode, port, dev);
+ break;
+
+ case DB9_CD32_PAD:
+
+ data=parport_read_data(port);
+
+ input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+
+ parport_write_control(port, 0x0a);
+
+ for (i = 0; i < 7; i++) {
+ data = parport_read_data(port);
+ parport_write_control(port, 0x02);
+ parport_write_control(port, 0x0a);
+ input_report_key(dev, db9_cd32_btn[i], ~data & DB9_FIRE2);
+ }
+
+ parport_write_control(port, 0x00);
+ break;
+ }
+
+ input_sync(dev);
+
+ mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
+}
+
+static int db9_open(struct input_dev *dev)
+{
+ struct db9 *db9 = dev->private;
+ struct parport *port = db9->pd->port;
+
+ if (!db9->used++) {
+ parport_claim(db9->pd);
+ parport_write_data(port, 0xff);
+ if (db9_reverse[db9->mode]) {
+ parport_data_reverse(port);
+ parport_write_control(port, DB9_NORMAL);
+ }
+ mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
+ }
+
+ return 0;
+}
+
+static void db9_close(struct input_dev *dev)
+{
+ struct db9 *db9 = dev->private;
+ struct parport *port = db9->pd->port;
+
+ if (!--db9->used) {
+ del_timer(&db9->timer);
+ parport_write_control(port, 0x00);
+ parport_data_forward(port);
+ parport_release(db9->pd);
+ }
+}
+
+static struct db9 __init *db9_probe(int *config, int nargs)
+{
+ struct db9 *db9;
+ struct parport *pp;
+ int i, j;
+
+ if (config[0] < 0)
+ return NULL;
+
+ if (nargs < 2) {
+ printk(KERN_ERR "db9.c: Device type must be specified.\n");
+ return NULL;
+ }
+
+ if (config[1] < 1 || config[1] >= DB9_MAX_PAD || !db9_buttons[config[1]]) {
+ printk(KERN_ERR "db9.c: bad config\n");
+ return NULL;
+ }
+
+ pp = parport_find_number(config[0]);
+ if (!pp) {
+ printk(KERN_ERR "db9.c: no such parport\n");
+ return NULL;
+ }
+
+ if (db9_bidirectional[config[1]]) {
+ if (!(pp->modes & PARPORT_MODE_TRISTATE)) {
+ printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
+ parport_put_port(pp);
+ return NULL;
+ }
+ }
+
+ if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL))) {
+ parport_put_port(pp);
+ return NULL;
+ }
+ memset(db9, 0, sizeof(struct db9));
+
+ db9->mode = config[1];
+ init_timer(&db9->timer);
+ db9->timer.data = (long) db9;
+ db9->timer.function = db9_timer;
+
+ db9->pd = parport_register_device(pp, "db9", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+ parport_put_port(pp);
+
+ if (!db9->pd) {
+ printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n");
+ kfree(db9);
+ return NULL;
+ }
+
+ for (i = 0; i < (min(db9_max_pads[db9->mode], DB9_MAX_DEVICES)); i++) {
+
+ sprintf(db9->phys[i], "%s/input%d", db9->pd->port->name, i);
+
+ db9->dev[i].private = db9;
+ db9->dev[i].open = db9_open;
+ db9->dev[i].close = db9_close;
+
+ db9->dev[i].name = db9_name[db9->mode];
+ db9->dev[i].phys = db9->phys[i];
+ db9->dev[i].id.bustype = BUS_PARPORT;
+ db9->dev[i].id.vendor = 0x0002;
+ db9->dev[i].id.product = config[1];
+ db9->dev[i].id.version = 0x0100;
+
+ db9->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ for (j = 0; j < db9_buttons[db9->mode]; j++)
+ set_bit(db9_btn[db9->mode][j], db9->dev[i].keybit);
+ for (j = 0; j < db9_num_axis[db9->mode]; j++) {
+ set_bit(db9_abs[j], db9->dev[i].absbit);
+ if (j < 2) {
+ db9->dev[i].absmin[db9_abs[j]] = -1;
+ db9->dev[i].absmax[db9_abs[j]] = 1;
+ } else {
+ db9->dev[i].absmin[db9_abs[j]] = 1;
+ db9->dev[i].absmax[db9_abs[j]] = 255;
+ db9->dev[i].absflat[db9_abs[j]] = 0;
+ }
+ }
+ input_register_device(db9->dev + i);
+ printk(KERN_INFO "input: %s on %s\n", db9->dev[i].name, db9->pd->port->name);
+ }
+
+ return db9;
+}
+
+static int __init db9_init(void)
+{
+ db9_base[0] = db9_probe(db9, db9_nargs);
+ db9_base[1] = db9_probe(db9_2, db9_nargs_2);
+ db9_base[2] = db9_probe(db9_3, db9_nargs_3);
+
+ if (db9_base[0] || db9_base[1] || db9_base[2])
+ return 0;
+
+ return -ENODEV;
+}
+
+static void __exit db9_exit(void)
+{
+ int i, j;
+
+ for (i = 0; i < 3; i++)
+ if (db9_base[i]) {
+ for (j = 0; j < min(db9_max_pads[db9_base[i]->mode], DB9_MAX_DEVICES); j++)
+ input_unregister_device(db9_base[i]->dev + j);
+ parport_unregister_device(db9_base[i]->pd);
+ }
+}
+
+module_init(db9_init);
+module_exit(db9_exit);
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
new file mode 100644
index 000000000000..8732f52bdd08
--- /dev/null
+++ b/drivers/input/joystick/gamecon.c
@@ -0,0 +1,697 @@
+/*
+ * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux
+ *
+ * Copyright (c) 1999-2004 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2004 Peter Nelson <rufus-kernel@hackish.org>
+ *
+ * Based on the work of:
+ * Andree Borrmann John Dahlstrom
+ * David Kuder Nathan Hand
+ */
+
+/*
+ * 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/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver");
+MODULE_LICENSE("GPL");
+
+static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc_nargs __initdata = 0;
+module_param_array_named(map, gc, int, &gc_nargs, 0);
+MODULE_PARM_DESC(map, "Describers first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)");
+
+static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc_nargs_2 __initdata = 0;
+module_param_array_named(map2, gc_2, int, &gc_nargs_2, 0);
+MODULE_PARM_DESC(map2, "Describers second set of devices");
+
+static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc_nargs_3 __initdata = 0;
+module_param_array_named(map3, gc_3, int, &gc_nargs_3, 0);
+MODULE_PARM_DESC(map3, "Describers third set of devices");
+
+__obsolete_setup("gc=");
+__obsolete_setup("gc_2=");
+__obsolete_setup("gc_3=");
+
+/* see also gs_psx_delay parameter in PSX support section */
+
+#define GC_SNES 1
+#define GC_NES 2
+#define GC_NES4 3
+#define GC_MULTI 4
+#define GC_MULTI2 5
+#define GC_N64 6
+#define GC_PSX 7
+#define GC_DDR 8
+
+#define GC_MAX 8
+
+#define GC_REFRESH_TIME HZ/100
+
+struct gc {
+ struct pardevice *pd;
+ struct input_dev dev[5];
+ struct timer_list timer;
+ unsigned char pads[GC_MAX + 1];
+ int used;
+ char phys[5][32];
+};
+
+static struct gc *gc_base[3];
+
+static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
+
+static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
+ "Multisystem 2-button joystick", "N64 controller", "PSX controller",
+ "PSX DDR controller" };
+/*
+ * N64 support.
+ */
+
+static unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 };
+static short gc_n64_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TRIGGER, BTN_START };
+
+#define GC_N64_LENGTH 32 /* N64 bit length, not including stop bit */
+#define GC_N64_REQUEST_LENGTH 37 /* transmit request sequence is 9 bits long */
+#define GC_N64_DELAY 133 /* delay between transmit request, and response ready (us) */
+#define GC_N64_REQUEST 0x1dd1111111ULL /* the request data command (encoded for 000000011) */
+#define GC_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */
+ /* GC_N64_DWS > 24 is known to fail */
+#define GC_N64_POWER_W 0xe2 /* power during write (transmit request) */
+#define GC_N64_POWER_R 0xfd /* power during read */
+#define GC_N64_OUT 0x1d /* output bits to the 4 pads */
+ /* Reading the main axes of any N64 pad is known to fail if the corresponding bit */
+ /* in GC_N64_OUT is pulled low on the output port (by any routine) for more */
+ /* than 123 us */
+#define GC_N64_CLOCK 0x02 /* clock bits for read */
+
+/*
+ * gc_n64_read_packet() reads an N64 packet.
+ * Each pad uses one bit per byte. So all pads connected to this port are read in parallel.
+ */
+
+static void gc_n64_read_packet(struct gc *gc, unsigned char *data)
+{
+ int i;
+ unsigned long flags;
+
+/*
+ * Request the pad to transmit data
+ */
+
+ local_irq_save(flags);
+ for (i = 0; i < GC_N64_REQUEST_LENGTH; i++) {
+ parport_write_data(gc->pd->port, GC_N64_POWER_W | ((GC_N64_REQUEST >> i) & 1 ? GC_N64_OUT : 0));
+ udelay(GC_N64_DWS);
+ }
+ local_irq_restore(flags);
+
+/*
+ * Wait for the pad response to be loaded into the 33-bit register of the adapter
+ */
+
+ udelay(GC_N64_DELAY);
+
+/*
+ * Grab data (ignoring the last bit, which is a stop bit)
+ */
+
+ for (i = 0; i < GC_N64_LENGTH; i++) {
+ parport_write_data(gc->pd->port, GC_N64_POWER_R);
+ data[i] = parport_read_status(gc->pd->port);
+ parport_write_data(gc->pd->port, GC_N64_POWER_R | GC_N64_CLOCK);
+ }
+
+/*
+ * We must wait 200 ms here for the controller to reinitialize before the next read request.
+ * No worries as long as gc_read is polled less frequently than this.
+ */
+
+}
+
+/*
+ * NES/SNES support.
+ */
+
+#define GC_NES_DELAY 6 /* Delay between bits - 6us */
+#define GC_NES_LENGTH 8 /* The NES pads use 8 bits of data */
+#define GC_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */
+
+#define GC_NES_POWER 0xfc
+#define GC_NES_CLOCK 0x01
+#define GC_NES_LATCH 0x02
+
+static unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 };
+static unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 };
+static short gc_snes_btn[] = { BTN_A, BTN_B, BTN_SELECT, BTN_START, BTN_X, BTN_Y, BTN_TL, BTN_TR };
+
+/*
+ * gc_nes_read_packet() reads a NES/SNES packet.
+ * Each pad uses one bit per byte. So all pads connected to
+ * this port are read in parallel.
+ */
+
+static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data)
+{
+ int i;
+
+ parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH);
+ udelay(GC_NES_DELAY * 2);
+ parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);
+
+ for (i = 0; i < length; i++) {
+ udelay(GC_NES_DELAY);
+ parport_write_data(gc->pd->port, GC_NES_POWER);
+ data[i] = parport_read_status(gc->pd->port) ^ 0x7f;
+ udelay(GC_NES_DELAY);
+ parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);
+ }
+}
+
+/*
+ * Multisystem joystick support
+ */
+
+#define GC_MULTI_LENGTH 5 /* Multi system joystick packet length is 5 */
+#define GC_MULTI2_LENGTH 6 /* One more bit for one more button */
+
+/*
+ * gc_multi_read_packet() reads a Multisystem joystick packet.
+ */
+
+static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ parport_write_data(gc->pd->port, ~(1 << i));
+ data[i] = parport_read_status(gc->pd->port) ^ 0x7f;
+ }
+}
+
+/*
+ * PSX support
+ *
+ * See documentation at:
+ * http://www.dim.com/~mackys/psxmemcard/ps-eng2.txt
+ * http://www.gamesx.com/controldata/psxcont/psxcont.htm
+ * ftp://milano.usal.es/pablo/
+ *
+ */
+
+#define GC_PSX_DELAY 25 /* 25 usec */
+#define GC_PSX_LENGTH 8 /* talk to the controller in bits */
+#define GC_PSX_BYTES 6 /* the maximum number of bytes to read off the controller */
+
+#define GC_PSX_MOUSE 1 /* Mouse */
+#define GC_PSX_NEGCON 2 /* NegCon */
+#define GC_PSX_NORMAL 4 /* Digital / Analog or Rumble in Digital mode */
+#define GC_PSX_ANALOG 5 /* Analog in Analog mode / Rumble in Green mode */
+#define GC_PSX_RUMBLE 7 /* Rumble in Red mode */
+
+#define GC_PSX_CLOCK 0x04 /* Pin 4 */
+#define GC_PSX_COMMAND 0x01 /* Pin 2 */
+#define GC_PSX_POWER 0xf8 /* Pins 5-9 */
+#define GC_PSX_SELECT 0x02 /* Pin 3 */
+
+#define GC_PSX_ID(x) ((x) >> 4) /* High nibble is device type */
+#define GC_PSX_LEN(x) (((x) & 0xf) << 1) /* Low nibble is length in bytes/2 */
+
+static int gc_psx_delay = GC_PSX_DELAY;
+module_param_named(psx_delay, gc_psx_delay, uint, 0);
+MODULE_PARM_DESC(psx_delay, "Delay when accessing Sony PSX controller (usecs)");
+
+__obsolete_setup("gc_psx_delay=");
+
+static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y };
+static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y,
+ BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR };
+static short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 };
+
+/*
+ * gc_psx_command() writes 8bit command and reads 8bit data from
+ * the psx pad.
+ */
+
+static void gc_psx_command(struct gc *gc, int b, unsigned char data[5])
+{
+ int i, j, cmd, read;
+ for (i = 0; i < 5; i++)
+ data[i] = 0;
+
+ for (i = 0; i < GC_PSX_LENGTH; i++, b >>= 1) {
+ cmd = (b & 1) ? GC_PSX_COMMAND : 0;
+ parport_write_data(gc->pd->port, cmd | GC_PSX_POWER);
+ udelay(gc_psx_delay);
+ read = parport_read_status(gc->pd->port) ^ 0x80;
+ for (j = 0; j < 5; j++)
+ data[j] |= (read & gc_status_bit[j] & (gc->pads[GC_PSX] | gc->pads[GC_DDR])) ? (1 << i) : 0;
+ parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER);
+ udelay(gc_psx_delay);
+ }
+}
+
+/*
+ * gc_psx_read_packet() reads a whole psx packet and returns
+ * device identifier code.
+ */
+
+static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_BYTES], unsigned char id[5])
+{
+ int i, j, max_len = 0;
+ unsigned long flags;
+ unsigned char data2[5];
+
+ parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); /* Select pad */
+ udelay(gc_psx_delay);
+ parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER); /* Deselect, begin command */
+ udelay(gc_psx_delay);
+
+ local_irq_save(flags);
+
+ gc_psx_command(gc, 0x01, data2); /* Access pad */
+ gc_psx_command(gc, 0x42, id); /* Get device ids */
+ gc_psx_command(gc, 0, data2); /* Dump status */
+
+ for (i =0; i < 5; i++) /* Find the longest pad */
+ if((gc_status_bit[i] & (gc->pads[GC_PSX] | gc->pads[GC_DDR]))
+ && (GC_PSX_LEN(id[i]) > max_len)
+ && (GC_PSX_LEN(id[i]) <= GC_PSX_BYTES))
+ max_len = GC_PSX_LEN(id[i]);
+
+ for (i = 0; i < max_len; i++) { /* Read in all the data */
+ gc_psx_command(gc, 0, data2);
+ for (j = 0; j < 5; j++)
+ data[j][i] = data2[j];
+ }
+
+ local_irq_restore(flags);
+
+ parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER);
+
+ for(i = 0; i < 5; i++) /* Set id's to the real value */
+ id[i] = GC_PSX_ID(id[i]);
+}
+
+/*
+ * gc_timer() reads and analyzes console pads data.
+ */
+
+#define GC_MAX_LENGTH GC_N64_LENGTH
+
+static void gc_timer(unsigned long private)
+{
+ struct gc *gc = (void *) private;
+ struct input_dev *dev = gc->dev;
+ unsigned char data[GC_MAX_LENGTH];
+ unsigned char data_psx[5][GC_PSX_BYTES];
+ int i, j, s;
+
+/*
+ * N64 pads - must be read first, any read confuses them for 200 us
+ */
+
+ if (gc->pads[GC_N64]) {
+
+ gc_n64_read_packet(gc, data);
+
+ for (i = 0; i < 5; i++) {
+
+ s = gc_status_bit[i];
+
+ if (s & gc->pads[GC_N64] & ~(data[8] | data[9])) {
+
+ signed char axes[2];
+ axes[0] = axes[1] = 0;
+
+ for (j = 0; j < 8; j++) {
+ if (data[23 - j] & s) axes[0] |= 1 << j;
+ if (data[31 - j] & s) axes[1] |= 1 << j;
+ }
+
+ input_report_abs(dev + i, ABS_X, axes[0]);
+ input_report_abs(dev + i, ABS_Y, -axes[1]);
+
+ input_report_abs(dev + i, ABS_HAT0X, !(s & data[6]) - !(s & data[7]));
+ input_report_abs(dev + i, ABS_HAT0Y, !(s & data[4]) - !(s & data[5]));
+
+ for (j = 0; j < 10; j++)
+ input_report_key(dev + i, gc_n64_btn[j], s & data[gc_n64_bytes[j]]);
+
+ input_sync(dev + i);
+ }
+ }
+ }
+
+/*
+ * NES and SNES pads
+ */
+
+ if (gc->pads[GC_NES] || gc->pads[GC_SNES]) {
+
+ gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data);
+
+ for (i = 0; i < 5; i++) {
+
+ s = gc_status_bit[i];
+
+ if (s & (gc->pads[GC_NES] | gc->pads[GC_SNES])) {
+ input_report_abs(dev + i, ABS_X, !(s & data[6]) - !(s & data[7]));
+ input_report_abs(dev + i, ABS_Y, !(s & data[4]) - !(s & data[5]));
+ }
+
+ if (s & gc->pads[GC_NES])
+ for (j = 0; j < 4; j++)
+ input_report_key(dev + i, gc_snes_btn[j], s & data[gc_nes_bytes[j]]);
+
+ if (s & gc->pads[GC_SNES])
+ for (j = 0; j < 8; j++)
+ input_report_key(dev + i, gc_snes_btn[j], s & data[gc_snes_bytes[j]]);
+
+ input_sync(dev + i);
+ }
+ }
+
+/*
+ * Multi and Multi2 joysticks
+ */
+
+ if (gc->pads[GC_MULTI] || gc->pads[GC_MULTI2]) {
+
+ gc_multi_read_packet(gc, gc->pads[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH, data);
+
+ for (i = 0; i < 5; i++) {
+
+ s = gc_status_bit[i];
+
+ if (s & (gc->pads[GC_MULTI] | gc->pads[GC_MULTI2])) {
+ input_report_abs(dev + i, ABS_X, !(s & data[2]) - !(s & data[3]));
+ input_report_abs(dev + i, ABS_Y, !(s & data[0]) - !(s & data[1]));
+ input_report_key(dev + i, BTN_TRIGGER, s & data[4]);
+ }
+
+ if (s & gc->pads[GC_MULTI2])
+ input_report_key(dev + i, BTN_THUMB, s & data[5]);
+
+ input_sync(dev + i);
+ }
+ }
+
+/*
+ * PSX controllers
+ */
+
+ if (gc->pads[GC_PSX] || gc->pads[GC_DDR]) {
+
+ gc_psx_read_packet(gc, data_psx, data);
+
+ for (i = 0; i < 5; i++) {
+ switch (data[i]) {
+
+ case GC_PSX_RUMBLE:
+
+ input_report_key(dev + i, BTN_THUMBL, ~data_psx[i][0] & 0x04);
+ input_report_key(dev + i, BTN_THUMBR, ~data_psx[i][0] & 0x02);
+
+ case GC_PSX_NEGCON:
+ case GC_PSX_ANALOG:
+
+ if(gc->pads[GC_DDR] & gc_status_bit[i]) {
+ for(j = 0; j < 4; j++)
+ input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j));
+ } else {
+ for (j = 0; j < 4; j++)
+ input_report_abs(dev + i, gc_psx_abs[j+2], data_psx[i][j + 2]);
+
+ input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128);
+ input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128);
+ }
+
+ for (j = 0; j < 8; j++)
+ input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j));
+
+ input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08);
+ input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01);
+
+ input_sync(dev + i);
+
+ break;
+
+ case GC_PSX_NORMAL:
+ if(gc->pads[GC_DDR] & gc_status_bit[i]) {
+ for(j = 0; j < 4; j++)
+ input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j));
+ } else {
+ input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128);
+ input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128);
+
+ /* for some reason if the extra axes are left unset they drift */
+ /* for (j = 0; j < 4; j++)
+ input_report_abs(dev + i, gc_psx_abs[j+2], 128);
+ * This needs to be debugged properly,
+ * maybe fuzz processing needs to be done in input_sync()
+ * --vojtech
+ */
+ }
+
+ for (j = 0; j < 8; j++)
+ input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j));
+
+ input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08);
+ input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01);
+
+ input_sync(dev + i);
+
+ break;
+
+ case 0: /* not a pad, ignore */
+ break;
+ }
+ }
+ }
+
+ mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
+}
+
+static int gc_open(struct input_dev *dev)
+{
+ struct gc *gc = dev->private;
+ if (!gc->used++) {
+ parport_claim(gc->pd);
+ parport_write_control(gc->pd->port, 0x04);
+ mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
+ }
+ return 0;
+}
+
+static void gc_close(struct input_dev *dev)
+{
+ struct gc *gc = dev->private;
+ if (!--gc->used) {
+ del_timer(&gc->timer);
+ parport_write_control(gc->pd->port, 0x00);
+ parport_release(gc->pd);
+ }
+}
+
+static struct gc __init *gc_probe(int *config, int nargs)
+{
+ struct gc *gc;
+ struct parport *pp;
+ int i, j;
+
+ if (config[0] < 0)
+ return NULL;
+
+ if (nargs < 2) {
+ printk(KERN_ERR "gamecon.c: at least one device must be specified\n");
+ return NULL;
+ }
+
+ pp = parport_find_number(config[0]);
+
+ if (!pp) {
+ printk(KERN_ERR "gamecon.c: no such parport\n");
+ return NULL;
+ }
+
+ if (!(gc = kmalloc(sizeof(struct gc), GFP_KERNEL))) {
+ parport_put_port(pp);
+ return NULL;
+ }
+ memset(gc, 0, sizeof(struct gc));
+
+ gc->pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+
+ parport_put_port(pp);
+
+ if (!gc->pd) {
+ printk(KERN_ERR "gamecon.c: parport busy already - lp.o loaded?\n");
+ kfree(gc);
+ return NULL;
+ }
+
+ parport_claim(gc->pd);
+
+ init_timer(&gc->timer);
+ gc->timer.data = (long) gc;
+ gc->timer.function = gc_timer;
+
+ for (i = 0; i < nargs - 1; i++) {
+
+ if (!config[i + 1])
+ continue;
+
+ if (config[i + 1] < 1 || config[i + 1] > GC_MAX) {
+ printk(KERN_WARNING "gamecon.c: Pad type %d unknown\n", config[i + 1]);
+ continue;
+ }
+
+ gc->dev[i].private = gc;
+ gc->dev[i].open = gc_open;
+ gc->dev[i].close = gc_close;
+
+ gc->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (j = 0; j < 2; j++) {
+ set_bit(ABS_X + j, gc->dev[i].absbit);
+ gc->dev[i].absmin[ABS_X + j] = -1;
+ gc->dev[i].absmax[ABS_X + j] = 1;
+ }
+
+ gc->pads[0] |= gc_status_bit[i];
+ gc->pads[config[i + 1]] |= gc_status_bit[i];
+
+ switch(config[i + 1]) {
+
+ case GC_N64:
+ for (j = 0; j < 10; j++)
+ set_bit(gc_n64_btn[j], gc->dev[i].keybit);
+
+ for (j = 0; j < 2; j++) {
+ set_bit(ABS_X + j, gc->dev[i].absbit);
+ gc->dev[i].absmin[ABS_X + j] = -127;
+ gc->dev[i].absmax[ABS_X + j] = 126;
+ gc->dev[i].absflat[ABS_X + j] = 2;
+ set_bit(ABS_HAT0X + j, gc->dev[i].absbit);
+ gc->dev[i].absmin[ABS_HAT0X + j] = -1;
+ gc->dev[i].absmax[ABS_HAT0X + j] = 1;
+ }
+
+ break;
+
+ case GC_SNES:
+ for (j = 4; j < 8; j++)
+ set_bit(gc_snes_btn[j], gc->dev[i].keybit);
+ case GC_NES:
+ for (j = 0; j < 4; j++)
+ set_bit(gc_snes_btn[j], gc->dev[i].keybit);
+ break;
+
+ case GC_MULTI2:
+ set_bit(BTN_THUMB, gc->dev[i].keybit);
+ case GC_MULTI:
+ set_bit(BTN_TRIGGER, gc->dev[i].keybit);
+ break;
+
+ case GC_PSX:
+ case GC_DDR:
+ if(config[i + 1] == GC_DDR) {
+ for (j = 0; j < 4; j++)
+ set_bit(gc_psx_ddr_btn[j], gc->dev[i].keybit);
+ } else {
+ for (j = 0; j < 6; j++) {
+ set_bit(gc_psx_abs[j], gc->dev[i].absbit);
+ gc->dev[i].absmin[gc_psx_abs[j]] = 4;
+ gc->dev[i].absmax[gc_psx_abs[j]] = 252;
+ gc->dev[i].absflat[gc_psx_abs[j]] = 2;
+ }
+ }
+
+ for (j = 0; j < 12; j++)
+ set_bit(gc_psx_btn[j], gc->dev[i].keybit);
+
+ break;
+ }
+
+ sprintf(gc->phys[i], "%s/input%d", gc->pd->port->name, i);
+
+ gc->dev[i].name = gc_names[config[i + 1]];
+ gc->dev[i].phys = gc->phys[i];
+ gc->dev[i].id.bustype = BUS_PARPORT;
+ gc->dev[i].id.vendor = 0x0001;
+ gc->dev[i].id.product = config[i + 1];
+ gc->dev[i].id.version = 0x0100;
+ }
+
+ parport_release(gc->pd);
+
+ if (!gc->pads[0]) {
+ parport_unregister_device(gc->pd);
+ kfree(gc);
+ return NULL;
+ }
+
+ for (i = 0; i < 5; i++)
+ if (gc->pads[0] & gc_status_bit[i]) {
+ input_register_device(gc->dev + i);
+ printk(KERN_INFO "input: %s on %s\n", gc->dev[i].name, gc->pd->port->name);
+ }
+
+ return gc;
+}
+
+static int __init gc_init(void)
+{
+ gc_base[0] = gc_probe(gc, gc_nargs);
+ gc_base[1] = gc_probe(gc_2, gc_nargs_2);
+ gc_base[2] = gc_probe(gc_3, gc_nargs_3);
+
+ if (gc_base[0] || gc_base[1] || gc_base[2])
+ return 0;
+
+ return -ENODEV;
+}
+
+static void __exit gc_exit(void)
+{
+ int i, j;
+
+ for (i = 0; i < 3; i++)
+ if (gc_base[i]) {
+ for (j = 0; j < 5; j++)
+ if (gc_base[i]->pads[0] & gc_status_bit[j])
+ input_unregister_device(gc_base[i]->dev + j);
+ parport_unregister_device(gc_base[i]->pd);
+ }
+}
+
+module_init(gc_init);
+module_exit(gc_exit);
diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c
new file mode 100644
index 000000000000..ad13f09a4e71
--- /dev/null
+++ b/drivers/input/joystick/gf2k.c
@@ -0,0 +1,380 @@
+/*
+ * $Id: gf2k.c,v 1.19 2002/01/22 20:27:43 vojtech Exp $
+ *
+ * Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Genius Flight 2000 joystick 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+
+#define DRIVER_DESC "Genius Flight 2000 joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GF2K_START 400 /* The time we wait for the first bit [400 us] */
+#define GF2K_STROBE 40 /* The time we wait for the first bit [40 us] */
+#define GF2K_TIMEOUT 4 /* Wait for everything to settle [4 ms] */
+#define GF2K_LENGTH 80 /* Max number of triplets in a packet */
+
+/*
+ * Genius joystick ids ...
+ */
+
+#define GF2K_ID_G09 1
+#define GF2K_ID_F30D 2
+#define GF2K_ID_F30 3
+#define GF2K_ID_F31D 4
+#define GF2K_ID_F305 5
+#define GF2K_ID_F23P 6
+#define GF2K_ID_F31 7
+#define GF2K_ID_MAX 7
+
+static char gf2k_length[] = { 40, 40, 40, 40, 40, 40, 40, 40 };
+static char gf2k_hat_to_axis[][2] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+static char *gf2k_names[] = {"", "Genius G-09D", "Genius F-30D", "Genius F-30", "Genius MaxFighter F-31D",
+ "Genius F-30-5", "Genius Flight2000 F-23", "Genius F-31"};
+static unsigned char gf2k_hats[] = { 0, 2, 0, 0, 2, 0, 2, 0 };
+static unsigned char gf2k_axes[] = { 0, 2, 0, 0, 4, 0, 4, 0 };
+static unsigned char gf2k_joys[] = { 0, 0, 0, 0,10, 0, 8, 0 };
+static unsigned char gf2k_pads[] = { 0, 6, 0, 0, 0, 0, 0, 0 };
+static unsigned char gf2k_lens[] = { 0,18, 0, 0,18, 0,18, 0 };
+
+static unsigned char gf2k_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_GAS, ABS_BRAKE };
+static short gf2k_btn_joy[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 };
+static short gf2k_btn_pad[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_START, BTN_SELECT };
+
+
+static short gf2k_seq_reset[] = { 240, 340, 0 };
+static short gf2k_seq_digital[] = { 590, 320, 860, 0 };
+
+struct gf2k {
+ struct gameport *gameport;
+ struct input_dev dev;
+ int reads;
+ int bads;
+ unsigned char id;
+ unsigned char length;
+ char phys[32];
+};
+
+/*
+ * gf2k_read_packet() reads a Genius Flight2000 packet.
+ */
+
+static int gf2k_read_packet(struct gameport *gameport, int length, char *data)
+{
+ unsigned char u, v;
+ int i;
+ unsigned int t, p;
+ unsigned long flags;
+
+ t = gameport_time(gameport, GF2K_START);
+ p = gameport_time(gameport, GF2K_STROBE);
+
+ i = 0;
+
+ local_irq_save(flags);
+
+ gameport_trigger(gameport);
+ v = gameport_read(gameport);
+
+ while (t > 0 && i < length) {
+ t--; u = v;
+ v = gameport_read(gameport);
+ if (v & ~u & 0x10) {
+ data[i++] = v >> 5;
+ t = p;
+ }
+ }
+
+ local_irq_restore(flags);
+
+ return i;
+}
+
+/*
+ * gf2k_trigger_seq() initializes a Genius Flight2000 joystick
+ * into digital mode.
+ */
+
+static void gf2k_trigger_seq(struct gameport *gameport, short *seq)
+{
+
+ unsigned long flags;
+ int i, t;
+
+ local_irq_save(flags);
+
+ i = 0;
+ do {
+ gameport_trigger(gameport);
+ t = gameport_time(gameport, GF2K_TIMEOUT * 1000);
+ while ((gameport_read(gameport) & 1) && t) t--;
+ udelay(seq[i]);
+ } while (seq[++i]);
+
+ gameport_trigger(gameport);
+
+ local_irq_restore(flags);
+}
+
+/*
+ * js_sw_get_bits() composes bits from the triplet buffer into a __u64.
+ * Parameter 'pos' is bit number inside packet where to start at, 'num' is number
+ * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits
+ * is number of bits per triplet.
+ */
+
+#define GB(p,n,s) gf2k_get_bits(data, p, n, s)
+
+static int gf2k_get_bits(unsigned char *buf, int pos, int num, int shift)
+{
+ __u64 data = 0;
+ int i;
+
+ for (i = 0; i < num / 3 + 2; i++)
+ data |= buf[pos / 3 + i] << (i * 3);
+ data >>= pos % 3;
+ data &= (1 << num) - 1;
+ data <<= shift;
+
+ return data;
+}
+
+static void gf2k_read(struct gf2k *gf2k, unsigned char *data)
+{
+ struct input_dev *dev = &gf2k->dev;
+ int i, t;
+
+ for (i = 0; i < 4 && i < gf2k_axes[gf2k->id]; i++)
+ input_report_abs(dev, gf2k_abs[i], GB(i<<3,8,0) | GB(i+46,1,8) | GB(i+50,1,9));
+
+ for (i = 0; i < 2 && i < gf2k_axes[gf2k->id] - 4; i++)
+ input_report_abs(dev, gf2k_abs[i], GB(i*9+60,8,0) | GB(i+54,1,9));
+
+ t = GB(40,4,0);
+
+ for (i = 0; i < gf2k_hats[gf2k->id]; i++)
+ input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]);
+
+ t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10);
+
+ for (i = 0; i < gf2k_joys[gf2k->id]; i++)
+ input_report_key(dev, gf2k_btn_joy[i], (t >> i) & 1);
+
+ for (i = 0; i < gf2k_pads[gf2k->id]; i++)
+ input_report_key(dev, gf2k_btn_pad[i], (t >> i) & 1);
+
+ input_sync(dev);
+}
+
+/*
+ * gf2k_poll() reads and analyzes Genius joystick data.
+ */
+
+static void gf2k_poll(struct gameport *gameport)
+{
+ struct gf2k *gf2k = gameport_get_drvdata(gameport);
+ unsigned char data[GF2K_LENGTH];
+
+ gf2k->reads++;
+
+ if (gf2k_read_packet(gf2k->gameport, gf2k_length[gf2k->id], data) < gf2k_length[gf2k->id])
+ gf2k->bads++;
+ else
+ gf2k_read(gf2k, data);
+}
+
+static int gf2k_open(struct input_dev *dev)
+{
+ struct gf2k *gf2k = dev->private;
+
+ gameport_start_polling(gf2k->gameport);
+ return 0;
+}
+
+static void gf2k_close(struct input_dev *dev)
+{
+ struct gf2k *gf2k = dev->private;
+
+ gameport_stop_polling(gf2k->gameport);
+}
+
+/*
+ * gf2k_connect() probes for Genius id joysticks.
+ */
+
+static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct gf2k *gf2k;
+ unsigned char data[GF2K_LENGTH];
+ int i, err;
+
+ if (!(gf2k = kcalloc(1, sizeof(struct gf2k), GFP_KERNEL)))
+ return -ENOMEM;
+
+ gf2k->gameport = gameport;
+
+ gameport_set_drvdata(gameport, gf2k);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ gf2k_trigger_seq(gameport, gf2k_seq_reset);
+
+ msleep(GF2K_TIMEOUT);
+
+ gf2k_trigger_seq(gameport, gf2k_seq_digital);
+
+ msleep(GF2K_TIMEOUT);
+
+ if (gf2k_read_packet(gameport, GF2K_LENGTH, data) < 12) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ if (!(gf2k->id = GB(7,2,0) | GB(3,3,2) | GB(0,3,5))) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+#ifdef RESET_WORKS
+ if ((gf2k->id != (GB(19,2,0) | GB(15,3,2) | GB(12,3,5))) ||
+ (gf2k->id != (GB(31,2,0) | GB(27,3,2) | GB(24,3,5)))) {
+ err = -ENODEV;
+ goto fail2;
+ }
+#else
+ gf2k->id = 6;
+#endif
+
+ if (gf2k->id > GF2K_ID_MAX || !gf2k_axes[gf2k->id]) {
+ printk(KERN_WARNING "gf2k.c: Not yet supported joystick on %s. [id: %d type:%s]\n",
+ gameport->phys, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]);
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ gameport_set_poll_handler(gameport, gf2k_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ sprintf(gf2k->phys, "%s/input0", gameport->phys);
+
+ gf2k->length = gf2k_lens[gf2k->id];
+
+ init_input_dev(&gf2k->dev);
+
+ gf2k->dev.private = gf2k;
+ gf2k->dev.open = gf2k_open;
+ gf2k->dev.close = gf2k_close;
+ gf2k->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ gf2k->dev.name = gf2k_names[gf2k->id];
+ gf2k->dev.phys = gf2k->phys;
+ gf2k->dev.id.bustype = BUS_GAMEPORT;
+ gf2k->dev.id.vendor = GAMEPORT_ID_VENDOR_GENIUS;
+ gf2k->dev.id.product = gf2k->id;
+ gf2k->dev.id.version = 0x0100;
+
+ for (i = 0; i < gf2k_axes[gf2k->id]; i++)
+ set_bit(gf2k_abs[i], gf2k->dev.absbit);
+
+ for (i = 0; i < gf2k_hats[gf2k->id]; i++) {
+ set_bit(ABS_HAT0X + i, gf2k->dev.absbit);
+ gf2k->dev.absmin[ABS_HAT0X + i] = -1;
+ gf2k->dev.absmax[ABS_HAT0X + i] = 1;
+ }
+
+ for (i = 0; i < gf2k_joys[gf2k->id]; i++)
+ set_bit(gf2k_btn_joy[i], gf2k->dev.keybit);
+
+ for (i = 0; i < gf2k_pads[gf2k->id]; i++)
+ set_bit(gf2k_btn_pad[i], gf2k->dev.keybit);
+
+ gf2k_read_packet(gameport, gf2k->length, data);
+ gf2k_read(gf2k, data);
+
+ for (i = 0; i < gf2k_axes[gf2k->id]; i++) {
+ gf2k->dev.absmax[gf2k_abs[i]] = (i < 2) ? gf2k->dev.abs[gf2k_abs[i]] * 2 - 32 :
+ gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32;
+ gf2k->dev.absmin[gf2k_abs[i]] = 32;
+ gf2k->dev.absfuzz[gf2k_abs[i]] = 8;
+ gf2k->dev.absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0;
+ }
+
+ input_register_device(&gf2k->dev);
+ printk(KERN_INFO "input: %s on %s\n", gf2k_names[gf2k->id], gameport->phys);
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(gf2k);
+ return err;
+}
+
+static void gf2k_disconnect(struct gameport *gameport)
+{
+ struct gf2k *gf2k = gameport_get_drvdata(gameport);
+
+ input_unregister_device(&gf2k->dev);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(gf2k);
+}
+
+static struct gameport_driver gf2k_drv = {
+ .driver = {
+ .name = "gf2k",
+ },
+ .description = DRIVER_DESC,
+ .connect = gf2k_connect,
+ .disconnect = gf2k_disconnect,
+};
+
+static int __init gf2k_init(void)
+{
+ gameport_register_driver(&gf2k_drv);
+ return 0;
+}
+
+static void __exit gf2k_exit(void)
+{
+ gameport_unregister_driver(&gf2k_drv);
+}
+
+module_init(gf2k_init);
+module_exit(gf2k_exit);
diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c
new file mode 100644
index 000000000000..d1500d2562d6
--- /dev/null
+++ b/drivers/input/joystick/grip.c
@@ -0,0 +1,422 @@
+/*
+ * $Id: grip.c,v 1.21 2002/01/22 20:27:57 vojtech Exp $
+ *
+ * Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Gravis/Kensington GrIP protocol joystick and gamepad 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/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC "Gravis GrIP protocol joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GRIP_MODE_GPP 1
+#define GRIP_MODE_BD 2
+#define GRIP_MODE_XT 3
+#define GRIP_MODE_DC 4
+
+#define GRIP_LENGTH_GPP 24
+#define GRIP_STROBE_GPP 200 /* 200 us */
+#define GRIP_LENGTH_XT 4
+#define GRIP_STROBE_XT 64 /* 64 us */
+#define GRIP_MAX_CHUNKS_XT 10
+#define GRIP_MAX_BITS_XT 30
+
+struct grip {
+ struct gameport *gameport;
+ struct input_dev dev[2];
+ unsigned char mode[2];
+ int reads;
+ int bads;
+ char phys[2][32];
+};
+
+static int grip_btn_gpp[] = { BTN_START, BTN_SELECT, BTN_TR2, BTN_Y, 0, BTN_TL2, BTN_A, BTN_B, BTN_X, 0, BTN_TL, BTN_TR, -1 };
+static int grip_btn_bd[] = { BTN_THUMB, BTN_THUMB2, BTN_TRIGGER, BTN_TOP, BTN_BASE, -1 };
+static int grip_btn_xt[] = { BTN_TRIGGER, BTN_THUMB, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_SELECT, BTN_START, BTN_MODE, -1 };
+static int grip_btn_dc[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, -1 };
+
+static int grip_abs_gpp[] = { ABS_X, ABS_Y, -1 };
+static int grip_abs_bd[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+static int grip_abs_xt[] = { ABS_X, ABS_Y, ABS_BRAKE, ABS_GAS, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, -1 };
+static int grip_abs_dc[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static char *grip_name[] = { NULL, "Gravis GamePad Pro", "Gravis Blackhawk Digital",
+ "Gravis Xterminator Digital", "Gravis Xterminator DualControl" };
+static int *grip_abs[] = { NULL, grip_abs_gpp, grip_abs_bd, grip_abs_xt, grip_abs_dc };
+static int *grip_btn[] = { NULL, grip_btn_gpp, grip_btn_bd, grip_btn_xt, grip_btn_dc };
+static char grip_anx[] = { 0, 0, 3, 5, 5 };
+static char grip_cen[] = { 0, 0, 2, 2, 4 };
+
+/*
+ * grip_gpp_read_packet() reads a Gravis GamePad Pro packet.
+ */
+
+static int grip_gpp_read_packet(struct gameport *gameport, int shift, unsigned int *data)
+{
+ unsigned long flags;
+ unsigned char u, v;
+ unsigned int t;
+ int i;
+
+ int strobe = gameport_time(gameport, GRIP_STROBE_GPP);
+
+ data[0] = 0;
+ t = strobe;
+ i = 0;
+
+ local_irq_save(flags);
+
+ v = gameport_read(gameport) >> shift;
+
+ do {
+ t--;
+ u = v; v = (gameport_read(gameport) >> shift) & 3;
+ if (~v & u & 1) {
+ data[0] |= (v >> 1) << i++;
+ t = strobe;
+ }
+ } while (i < GRIP_LENGTH_GPP && t > 0);
+
+ local_irq_restore(flags);
+
+ if (i < GRIP_LENGTH_GPP) return -1;
+
+ for (i = 0; i < GRIP_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++)
+ data[0] = data[0] >> 1 | (data[0] & 1) << (GRIP_LENGTH_GPP - 1);
+
+ return -(i == GRIP_LENGTH_GPP);
+}
+
+/*
+ * grip_xt_read_packet() reads a Gravis Xterminator packet.
+ */
+
+static int grip_xt_read_packet(struct gameport *gameport, int shift, unsigned int *data)
+{
+ unsigned int i, j, buf, crc;
+ unsigned char u, v, w;
+ unsigned long flags;
+ unsigned int t;
+ char status;
+
+ int strobe = gameport_time(gameport, GRIP_STROBE_XT);
+
+ data[0] = data[1] = data[2] = data[3] = 0;
+ status = buf = i = j = 0;
+ t = strobe;
+
+ local_irq_save(flags);
+
+ v = w = (gameport_read(gameport) >> shift) & 3;
+
+ do {
+ t--;
+ u = (gameport_read(gameport) >> shift) & 3;
+
+ if (u ^ v) {
+
+ if ((u ^ v) & 1) {
+ buf = (buf << 1) | (u >> 1);
+ t = strobe;
+ i++;
+ } else
+
+ if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) {
+ if (i == 20) {
+ crc = buf ^ (buf >> 7) ^ (buf >> 14);
+ if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) {
+ data[buf >> 18] = buf >> 4;
+ status |= 1 << (buf >> 18);
+ }
+ j++;
+ }
+ t = strobe;
+ buf = 0;
+ i = 0;
+ }
+ w = v;
+ v = u;
+ }
+
+ } while (status != 0xf && i < GRIP_MAX_BITS_XT && j < GRIP_MAX_CHUNKS_XT && t > 0);
+
+ local_irq_restore(flags);
+
+ return -(status != 0xf);
+}
+
+/*
+ * grip_timer() repeatedly polls the joysticks and generates events.
+ */
+
+static void grip_poll(struct gameport *gameport)
+{
+ struct grip *grip = gameport_get_drvdata(gameport);
+ unsigned int data[GRIP_LENGTH_XT];
+ struct input_dev *dev;
+ int i, j;
+
+ for (i = 0; i < 2; i++) {
+
+ dev = grip->dev + i;
+ grip->reads++;
+
+ switch (grip->mode[i]) {
+
+ case GRIP_MODE_GPP:
+
+ if (grip_gpp_read_packet(grip->gameport, (i << 1) + 4, data)) {
+ grip->bads++;
+ break;
+ }
+
+ input_report_abs(dev, ABS_X, ((*data >> 15) & 1) - ((*data >> 16) & 1));
+ input_report_abs(dev, ABS_Y, ((*data >> 13) & 1) - ((*data >> 12) & 1));
+
+ for (j = 0; j < 12; j++)
+ if (grip_btn_gpp[j])
+ input_report_key(dev, grip_btn_gpp[j], (*data >> j) & 1);
+
+ break;
+
+ case GRIP_MODE_BD:
+
+ if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+ grip->bads++;
+ break;
+ }
+
+ input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f);
+ input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f));
+ input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+ input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1));
+ input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+
+ for (j = 0; j < 5; j++)
+ input_report_key(dev, grip_btn_bd[j], (data[3] >> (j + 4)) & 1);
+
+ break;
+
+ case GRIP_MODE_XT:
+
+ if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+ grip->bads++;
+ break;
+ }
+
+ input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f);
+ input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f));
+ input_report_abs(dev, ABS_BRAKE, (data[1] >> 2) & 0x3f);
+ input_report_abs(dev, ABS_GAS, (data[1] >> 8) & 0x3f);
+ input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+ input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1));
+ input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+ input_report_abs(dev, ABS_HAT1X, ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1));
+ input_report_abs(dev, ABS_HAT1Y, ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1));
+
+ for (j = 0; j < 11; j++)
+ input_report_key(dev, grip_btn_xt[j], (data[3] >> (j + 3)) & 1);
+ break;
+
+ case GRIP_MODE_DC:
+
+ if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+ grip->bads++;
+ break;
+ }
+
+ input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f);
+ input_report_abs(dev, ABS_Y, (data[0] >> 8) & 0x3f);
+ input_report_abs(dev, ABS_RX, (data[1] >> 2) & 0x3f);
+ input_report_abs(dev, ABS_RY, (data[1] >> 8) & 0x3f);
+ input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+ input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1));
+ input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+
+ for (j = 0; j < 9; j++)
+ input_report_key(dev, grip_btn_dc[j], (data[3] >> (j + 3)) & 1);
+ break;
+
+
+ }
+
+ input_sync(dev);
+ }
+}
+
+static int grip_open(struct input_dev *dev)
+{
+ struct grip *grip = dev->private;
+
+ gameport_start_polling(grip->gameport);
+ return 0;
+}
+
+static void grip_close(struct input_dev *dev)
+{
+ struct grip *grip = dev->private;
+
+ gameport_stop_polling(grip->gameport);
+}
+
+static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct grip *grip;
+ unsigned int data[GRIP_LENGTH_XT];
+ int i, j, t;
+ int err;
+
+ if (!(grip = kcalloc(1, sizeof(struct grip), GFP_KERNEL)))
+ return -ENOMEM;
+
+ grip->gameport = gameport;
+
+ gameport_set_drvdata(gameport, grip);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ for (i = 0; i < 2; i++) {
+ if (!grip_gpp_read_packet(gameport, (i << 1) + 4, data)) {
+ grip->mode[i] = GRIP_MODE_GPP;
+ continue;
+ }
+ if (!grip_xt_read_packet(gameport, (i << 1) + 4, data)) {
+ if (!(data[3] & 7)) {
+ grip->mode[i] = GRIP_MODE_BD;
+ continue;
+ }
+ if (!(data[2] & 0xf0)) {
+ grip->mode[i] = GRIP_MODE_XT;
+ continue;
+ }
+ grip->mode[i] = GRIP_MODE_DC;
+ continue;
+ }
+ }
+
+ if (!grip->mode[0] && !grip->mode[1]) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ gameport_set_poll_handler(gameport, grip_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ for (i = 0; i < 2; i++)
+ if (grip->mode[i]) {
+
+ sprintf(grip->phys[i], "%s/input%d", gameport->phys, i);
+
+ grip->dev[i].private = grip;
+
+ grip->dev[i].open = grip_open;
+ grip->dev[i].close = grip_close;
+
+ grip->dev[i].name = grip_name[grip->mode[i]];
+ grip->dev[i].phys = grip->phys[i];
+ grip->dev[i].id.bustype = BUS_GAMEPORT;
+ grip->dev[i].id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
+ grip->dev[i].id.product = grip->mode[i];
+ grip->dev[i].id.version = 0x0100;
+
+ grip->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (j = 0; (t = grip_abs[grip->mode[i]][j]) >= 0; j++) {
+
+ if (j < grip_cen[grip->mode[i]])
+ input_set_abs_params(&grip->dev[i], t, 14, 52, 1, 2);
+ else if (j < grip_anx[grip->mode[i]])
+ input_set_abs_params(&grip->dev[i], t, 3, 57, 1, 0);
+ else
+ input_set_abs_params(&grip->dev[i], t, -1, 1, 0, 0);
+ }
+
+ for (j = 0; (t = grip_btn[grip->mode[i]][j]) >= 0; j++)
+ if (t > 0)
+ set_bit(t, grip->dev[i].keybit);
+
+ printk(KERN_INFO "input: %s on %s\n",
+ grip_name[grip->mode[i]], gameport->phys);
+ input_register_device(grip->dev + i);
+ }
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(grip);
+ return err;
+}
+
+static void grip_disconnect(struct gameport *gameport)
+{
+ struct grip *grip = gameport_get_drvdata(gameport);
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (grip->mode[i])
+ input_unregister_device(grip->dev + i);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(grip);
+}
+
+static struct gameport_driver grip_drv = {
+ .driver = {
+ .name = "grip",
+ },
+ .description = DRIVER_DESC,
+ .connect = grip_connect,
+ .disconnect = grip_disconnect,
+};
+
+static int __init grip_init(void)
+{
+ gameport_register_driver(&grip_drv);
+ return 0;
+}
+
+static void __exit grip_exit(void)
+{
+ gameport_unregister_driver(&grip_drv);
+}
+
+module_init(grip_init);
+module_exit(grip_exit);
diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c
new file mode 100644
index 000000000000..42e5005d621f
--- /dev/null
+++ b/drivers/input/joystick/grip_mp.c
@@ -0,0 +1,677 @@
+/*
+ * $Id: grip_mp.c,v 1.9 2002/07/20 19:28:45 bonnland Exp $
+ *
+ * Driver for the Gravis Grip Multiport, a gamepad "hub" that
+ * connects up to four 9-pin digital gamepads/joysticks.
+ * Driver tested on SMP and UP kernel versions 2.4.18-4 and 2.4.18-5.
+ *
+ * Thanks to Chris Gassib for helpful advice.
+ *
+ * Copyright (c) 2002 Brian Bonnlander, Bill Soudan
+ * Copyright (c) 1998-2000 Vojtech Pavlik
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+
+#define DRIVER_DESC "Gravis Grip Multiport driver"
+
+MODULE_AUTHOR("Brian Bonnlander");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#ifdef GRIP_DEBUG
+#define dbg(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+/*
+ * Grip multiport state
+ */
+
+struct grip_mp {
+ struct gameport *gameport;
+ struct input_dev dev[4];
+ int mode[4];
+ int registered[4];
+ int reads;
+ int bads;
+
+ /* individual gamepad states */
+ int buttons[4];
+ int xaxes[4];
+ int yaxes[4];
+ int dirty[4]; /* has the state been updated? */
+};
+
+/*
+ * Multiport packet interpretation
+ */
+
+#define PACKET_FULL 0x80000000 /* packet is full */
+#define PACKET_IO_FAST 0x40000000 /* 3 bits per gameport read */
+#define PACKET_IO_SLOW 0x20000000 /* 1 bit per gameport read */
+#define PACKET_MP_MORE 0x04000000 /* multiport wants to send more */
+#define PACKET_MP_DONE 0x02000000 /* multiport done sending */
+
+/*
+ * Packet status code interpretation
+ */
+
+#define IO_GOT_PACKET 0x0100 /* Got a packet */
+#define IO_MODE_FAST 0x0200 /* Used 3 data bits per gameport read */
+#define IO_SLOT_CHANGE 0x0800 /* Multiport physical slot status changed */
+#define IO_DONE 0x1000 /* Multiport is done sending packets */
+#define IO_RETRY 0x4000 /* Try again later to get packet */
+#define IO_RESET 0x8000 /* Force multiport to resend all packets */
+
+/*
+ * Gamepad configuration data. Other 9-pin digital joystick devices
+ * may work with the multiport, so this may not be an exhaustive list!
+ * Commodore 64 joystick remains untested.
+ */
+
+#define GRIP_INIT_DELAY 2000 /* 2 ms */
+
+#define GRIP_MODE_NONE 0
+#define GRIP_MODE_RESET 1
+#define GRIP_MODE_GP 2
+#define GRIP_MODE_C64 3
+
+static int grip_btn_gp[] = { BTN_TR, BTN_TL, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, -1 };
+static int grip_btn_c64[] = { BTN_JOYSTICK, -1 };
+
+static int grip_abs_gp[] = { ABS_X, ABS_Y, -1 };
+static int grip_abs_c64[] = { ABS_X, ABS_Y, -1 };
+
+static int *grip_abs[] = { NULL, NULL, grip_abs_gp, grip_abs_c64 };
+static int *grip_btn[] = { NULL, NULL, grip_btn_gp, grip_btn_c64 };
+
+static char *grip_name[] = { NULL, NULL, "Gravis Grip Pad", "Commodore 64 Joystick" };
+
+static const int init_seq[] = {
+ 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+ 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
+ 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1 };
+
+/* Maps multiport directional values to X,Y axis values (each axis encoded in 3 bits) */
+
+static int axis_map[] = { 5, 9, 1, 5, 6, 10, 2, 6, 4, 8, 0, 4, 5, 9, 1, 5 };
+
+static void register_slot(int i, struct grip_mp *grip);
+
+/*
+ * Returns whether an odd or even number of bits are on in pkt.
+ */
+
+static int bit_parity(u32 pkt)
+{
+ int x = pkt ^ (pkt >> 16);
+ x ^= x >> 8;
+ x ^= x >> 4;
+ x ^= x >> 2;
+ x ^= x >> 1;
+ return x & 1;
+}
+
+/*
+ * Poll gameport; return true if all bits set in 'onbits' are on and
+ * all bits set in 'offbits' are off.
+ */
+
+static inline int poll_until(u8 onbits, u8 offbits, int u_sec, struct gameport* gp, u8 *data)
+{
+ int i, nloops;
+
+ nloops = gameport_time(gp, u_sec);
+ for (i = 0; i < nloops; i++) {
+ *data = gameport_read(gp);
+ if ((*data & onbits) == onbits &&
+ (~(*data) & offbits) == offbits)
+ return 1;
+ }
+ dbg("gameport timed out after %d microseconds.\n", u_sec);
+ return 0;
+}
+
+/*
+ * Gets a 28-bit packet from the multiport.
+ *
+ * After getting a packet successfully, commands encoded by sendcode may
+ * be sent to the multiport.
+ *
+ * The multiport clock value is reflected in gameport bit B4.
+ *
+ * Returns a packet status code indicating whether packet is valid, the transfer
+ * mode, and any error conditions.
+ *
+ * sendflags: current I/O status
+ * sendcode: data to send to the multiport if sendflags is nonzero
+ */
+
+static int mp_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
+{
+ u8 raw_data; /* raw data from gameport */
+ u8 data_mask; /* packet data bits from raw_data */
+ u32 pkt; /* packet temporary storage */
+ int bits_per_read; /* num packet bits per gameport read */
+ int portvals = 0; /* used for port value sanity check */
+ int i;
+
+ /* Gameport bits B0, B4, B5 should first be off, then B4 should come on. */
+
+ *packet = 0;
+ raw_data = gameport_read(gameport);
+ if (raw_data & 1)
+ return IO_RETRY;
+
+ for (i = 0; i < 64; i++) {
+ raw_data = gameport_read(gameport);
+ portvals |= 1 << ((raw_data >> 4) & 3); /* Demux B4, B5 */
+ }
+
+ if (portvals == 1) { /* B4, B5 off */
+ raw_data = gameport_read(gameport);
+ portvals = raw_data & 0xf0;
+
+ if (raw_data & 0x31)
+ return IO_RESET;
+ gameport_trigger(gameport);
+
+ if (!poll_until(0x10, 0, 308, gameport, &raw_data))
+ return IO_RESET;
+ } else
+ return IO_RETRY;
+
+ /* Determine packet transfer mode and prepare for packet construction. */
+
+ if (raw_data & 0x20) { /* 3 data bits/read */
+ portvals |= raw_data >> 4; /* Compare B4-B7 before & after trigger */
+
+ if (portvals != 0xb)
+ return 0;
+ data_mask = 7;
+ bits_per_read = 3;
+ pkt = (PACKET_FULL | PACKET_IO_FAST) >> 28;
+ } else { /* 1 data bit/read */
+ data_mask = 1;
+ bits_per_read = 1;
+ pkt = (PACKET_FULL | PACKET_IO_SLOW) >> 28;
+ }
+
+ /* Construct a packet. Final data bits must be zero. */
+
+ while (1) {
+ if (!poll_until(0, 0x10, 77, gameport, &raw_data))
+ return IO_RESET;
+ raw_data = (raw_data >> 5) & data_mask;
+
+ if (pkt & PACKET_FULL)
+ break;
+ pkt = (pkt << bits_per_read) | raw_data;
+
+ if (!poll_until(0x10, 0, 77, gameport, &raw_data))
+ return IO_RESET;
+ }
+
+ if (raw_data)
+ return IO_RESET;
+
+ /* If 3 bits/read used, drop from 30 bits to 28. */
+
+ if (bits_per_read == 3) {
+ pkt = (pkt & 0xffff0000) | ((pkt << 1) & 0xffff);
+ pkt = (pkt >> 2) | 0xf0000000;
+ }
+
+ if (bit_parity(pkt) == 1)
+ return IO_RESET;
+
+ /* Acknowledge packet receipt */
+
+ if (!poll_until(0x30, 0, 77, gameport, &raw_data))
+ return IO_RESET;
+
+ raw_data = gameport_read(gameport);
+
+ if (raw_data & 1)
+ return IO_RESET;
+
+ gameport_trigger(gameport);
+
+ if (!poll_until(0, 0x20, 77, gameport, &raw_data))
+ return IO_RESET;
+
+ /* Return if we just wanted the packet or multiport wants to send more */
+
+ *packet = pkt;
+ if ((sendflags == 0) || ((sendflags & IO_RETRY) && !(pkt & PACKET_MP_DONE)))
+ return IO_GOT_PACKET;
+
+ if (pkt & PACKET_MP_MORE)
+ return IO_GOT_PACKET | IO_RETRY;
+
+ /* Multiport is done sending packets and is ready to receive data */
+
+ if (!poll_until(0x20, 0, 77, gameport, &raw_data))
+ return IO_GOT_PACKET | IO_RESET;
+
+ raw_data = gameport_read(gameport);
+ if (raw_data & 1)
+ return IO_GOT_PACKET | IO_RESET;
+
+ /* Trigger gameport based on bits in sendcode */
+
+ gameport_trigger(gameport);
+ do {
+ if (!poll_until(0x20, 0x10, 116, gameport, &raw_data))
+ return IO_GOT_PACKET | IO_RESET;
+
+ if (!poll_until(0x30, 0, 193, gameport, &raw_data))
+ return IO_GOT_PACKET | IO_RESET;
+
+ if (raw_data & 1)
+ return IO_GOT_PACKET | IO_RESET;
+
+ if (sendcode & 1)
+ gameport_trigger(gameport);
+
+ sendcode >>= 1;
+ } while (sendcode);
+
+ return IO_GOT_PACKET | IO_MODE_FAST;
+}
+
+/*
+ * Disables and restores interrupts for mp_io(), which does the actual I/O.
+ */
+
+static int multiport_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
+{
+ int status;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ status = mp_io(gameport, sendflags, sendcode, packet);
+ local_irq_restore(flags);
+
+ return status;
+}
+
+/*
+ * Puts multiport into digital mode. Multiport LED turns green.
+ *
+ * Returns true if a valid digital packet was received, false otherwise.
+ */
+
+static int dig_mode_start(struct gameport *gameport, u32 *packet)
+{
+ int i, seq_len = sizeof(init_seq)/sizeof(int);
+ int flags, tries = 0, bads = 0;
+
+ for (i = 0; i < seq_len; i++) { /* Send magic sequence */
+ if (init_seq[i])
+ gameport_trigger(gameport);
+ udelay(GRIP_INIT_DELAY);
+ }
+
+ for (i = 0; i < 16; i++) /* Wait for multiport to settle */
+ udelay(GRIP_INIT_DELAY);
+
+ while (tries < 64 && bads < 8) { /* Reset multiport and try getting a packet */
+
+ flags = multiport_io(gameport, IO_RESET, 0x27, packet);
+
+ if (flags & IO_MODE_FAST)
+ return 1;
+
+ if (flags & IO_RETRY)
+ tries++;
+ else
+ bads++;
+ }
+ return 0;
+}
+
+/*
+ * Packet structure: B0-B15 => gamepad state
+ * B16-B20 => gamepad device type
+ * B21-B24 => multiport slot index (1-4)
+ *
+ * Known device types: 0x1f (grip pad), 0x0 (no device). Others may exist.
+ *
+ * Returns the packet status.
+ */
+
+static int get_and_decode_packet(struct grip_mp *grip, int flags)
+{
+ u32 packet;
+ int joytype = 0;
+ int slot = 0;
+
+ /* Get a packet and check for validity */
+
+ flags &= IO_RESET | IO_RETRY;
+ flags = multiport_io(grip->gameport, flags, 0, &packet);
+ grip->reads++;
+
+ if (packet & PACKET_MP_DONE)
+ flags |= IO_DONE;
+
+ if (flags && !(flags & IO_GOT_PACKET)) {
+ grip->bads++;
+ return flags;
+ }
+
+ /* Ignore non-gamepad packets, e.g. multiport hardware version */
+
+ slot = ((packet >> 21) & 0xf) - 1;
+ if ((slot < 0) || (slot > 3))
+ return flags;
+
+ /*
+ * Handle "reset" packets, which occur at startup, and when gamepads
+ * are removed or plugged in. May contain configuration of a new gamepad.
+ */
+
+ joytype = (packet >> 16) & 0x1f;
+ if (!joytype) {
+
+ if (grip->registered[slot]) {
+ printk(KERN_INFO "grip_mp: removing %s, slot %d\n",
+ grip_name[grip->mode[slot]], slot);
+ input_unregister_device(grip->dev + slot);
+ grip->registered[slot] = 0;
+ }
+ dbg("Reset: grip multiport slot %d\n", slot);
+ grip->mode[slot] = GRIP_MODE_RESET;
+ flags |= IO_SLOT_CHANGE;
+ return flags;
+ }
+
+ /* Interpret a grip pad packet */
+
+ if (joytype == 0x1f) {
+
+ int dir = (packet >> 8) & 0xf; /* eight way directional value */
+ grip->buttons[slot] = (~packet) & 0xff;
+ grip->yaxes[slot] = ((axis_map[dir] >> 2) & 3) - 1;
+ grip->xaxes[slot] = (axis_map[dir] & 3) - 1;
+ grip->dirty[slot] = 1;
+
+ if (grip->mode[slot] == GRIP_MODE_RESET)
+ flags |= IO_SLOT_CHANGE;
+
+ grip->mode[slot] = GRIP_MODE_GP;
+
+ if (!grip->registered[slot]) {
+ dbg("New Grip pad in multiport slot %d.\n", slot);
+ register_slot(slot, grip);
+ }
+ return flags;
+ }
+
+ /* Handle non-grip device codes. For now, just print diagnostics. */
+
+ {
+ static int strange_code = 0;
+ if (strange_code != joytype) {
+ printk(KERN_INFO "Possible non-grip pad/joystick detected.\n");
+ printk(KERN_INFO "Got joy type 0x%x and packet 0x%x.\n", joytype, packet);
+ strange_code = joytype;
+ }
+ }
+ return flags;
+}
+
+/*
+ * Returns true if all multiport slot states appear valid.
+ */
+
+static int slots_valid(struct grip_mp *grip)
+{
+ int flags, slot, invalid = 0, active = 0;
+
+ flags = get_and_decode_packet(grip, 0);
+ if (!(flags & IO_GOT_PACKET))
+ return 0;
+
+ for (slot = 0; slot < 4; slot++) {
+ if (grip->mode[slot] == GRIP_MODE_RESET)
+ invalid = 1;
+ if (grip->mode[slot] != GRIP_MODE_NONE)
+ active = 1;
+ }
+
+ /* Return true if no active slot but multiport sent all its data */
+ if (!active)
+ return (flags & IO_DONE) ? 1 : 0;
+
+ /* Return false if invalid device code received */
+ return invalid ? 0 : 1;
+}
+
+/*
+ * Returns whether the multiport was placed into digital mode and
+ * able to communicate its state successfully.
+ */
+
+static int multiport_init(struct grip_mp *grip)
+{
+ int dig_mode, initialized = 0, tries = 0;
+ u32 packet;
+
+ dig_mode = dig_mode_start(grip->gameport, &packet);
+ while (!dig_mode && tries < 4) {
+ dig_mode = dig_mode_start(grip->gameport, &packet);
+ tries++;
+ }
+
+ if (dig_mode)
+ dbg("multiport_init(): digital mode activated.\n");
+ else {
+ dbg("multiport_init(): unable to activate digital mode.\n");
+ return 0;
+ }
+
+ /* Get packets, store multiport state, and check state's validity */
+ for (tries = 0; tries < 4096; tries++) {
+ if ( slots_valid(grip) ) {
+ initialized = 1;
+ break;
+ }
+ }
+ dbg("multiport_init(): initialized == %d\n", initialized);
+ return initialized;
+}
+
+/*
+ * Reports joystick state to the linux input layer.
+ */
+
+static void report_slot(struct grip_mp *grip, int slot)
+{
+ struct input_dev *dev = &(grip->dev[slot]);
+ int i, buttons = grip->buttons[slot];
+
+ /* Store button states with linux input driver */
+
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, grip_btn_gp[i], (buttons >> i) & 1);
+
+ /* Store axis states with linux driver */
+
+ input_report_abs(dev, ABS_X, grip->xaxes[slot]);
+ input_report_abs(dev, ABS_Y, grip->yaxes[slot]);
+
+ /* Tell the receiver of the events to process them */
+
+ input_sync(dev);
+
+ grip->dirty[slot] = 0;
+}
+
+/*
+ * Get the multiport state.
+ */
+
+static void grip_poll(struct gameport *gameport)
+{
+ struct grip_mp *grip = gameport_get_drvdata(gameport);
+ int i, npkts, flags;
+
+ for (npkts = 0; npkts < 4; npkts++) {
+ flags = IO_RETRY;
+ for (i = 0; i < 32; i++) {
+ flags = get_and_decode_packet(grip, flags);
+ if ((flags & IO_GOT_PACKET) || !(flags & IO_RETRY))
+ break;
+ }
+ if (flags & IO_DONE)
+ break;
+ }
+
+ for (i = 0; i < 4; i++)
+ if (grip->dirty[i])
+ report_slot(grip, i);
+}
+
+/*
+ * Called when a joystick device file is opened
+ */
+
+static int grip_open(struct input_dev *dev)
+{
+ struct grip_mp *grip = dev->private;
+
+ gameport_start_polling(grip->gameport);
+ return 0;
+}
+
+/*
+ * Called when a joystick device file is closed
+ */
+
+static void grip_close(struct input_dev *dev)
+{
+ struct grip_mp *grip = dev->private;
+
+ gameport_start_polling(grip->gameport);
+}
+
+/*
+ * Tell the linux input layer about a newly plugged-in gamepad.
+ */
+
+static void register_slot(int slot, struct grip_mp *grip)
+{
+ int j, t;
+
+ grip->dev[slot].private = grip;
+ grip->dev[slot].open = grip_open;
+ grip->dev[slot].close = grip_close;
+ grip->dev[slot].name = grip_name[grip->mode[slot]];
+ grip->dev[slot].id.bustype = BUS_GAMEPORT;
+ grip->dev[slot].id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
+ grip->dev[slot].id.product = 0x0100 + grip->mode[slot];
+ grip->dev[slot].id.version = 0x0100;
+ grip->dev[slot].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (j = 0; (t = grip_abs[grip->mode[slot]][j]) >= 0; j++)
+ input_set_abs_params(&grip->dev[slot], t, -1, 1, 0, 0);
+
+ for (j = 0; (t = grip_btn[grip->mode[slot]][j]) >= 0; j++)
+ if (t > 0)
+ set_bit(t, grip->dev[slot].keybit);
+
+ input_register_device(grip->dev + slot);
+ grip->registered[slot] = 1;
+
+ if (grip->dirty[slot]) /* report initial state, if any */
+ report_slot(grip, slot);
+
+ printk(KERN_INFO "grip_mp: added %s, slot %d\n",
+ grip_name[grip->mode[slot]], slot);
+}
+
+static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct grip_mp *grip;
+ int err;
+
+ if (!(grip = kcalloc(1, sizeof(struct grip_mp), GFP_KERNEL)))
+ return -ENOMEM;
+
+ grip->gameport = gameport;
+
+ gameport_set_drvdata(gameport, grip);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ gameport_set_poll_handler(gameport, grip_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ if (!multiport_init(grip)) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ if (!grip->mode[0] && !grip->mode[1] && !grip->mode[2] && !grip->mode[3]) {
+ /* nothing plugged in */
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(grip);
+ return err;
+}
+
+static void grip_disconnect(struct gameport *gameport)
+{
+ struct grip_mp *grip = gameport_get_drvdata(gameport);
+ int i;
+
+ for (i = 0; i < 4; i++)
+ if (grip->registered[i])
+ input_unregister_device(grip->dev + i);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(grip);
+}
+
+static struct gameport_driver grip_drv = {
+ .driver = {
+ .name = "grip_mp",
+ },
+ .description = DRIVER_DESC,
+ .connect = grip_connect,
+ .disconnect = grip_disconnect,
+};
+
+static int __init grip_init(void)
+{
+ gameport_register_driver(&grip_drv);
+ return 0;
+}
+
+static void __exit grip_exit(void)
+{
+ gameport_unregister_driver(&grip_drv);
+}
+
+module_init(grip_init);
+module_exit(grip_exit);
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
new file mode 100644
index 000000000000..f93da7bc082d
--- /dev/null
+++ b/drivers/input/joystick/guillemot.c
@@ -0,0 +1,289 @@
+/*
+ * $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>
+
+#define DRIVER_DESC "Guillemot Digital joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GUILLEMOT_MAX_START 600 /* 600 us */
+#define GUILLEMOT_MAX_STROBE 60 /* 60 us */
+#define GUILLEMOT_MAX_LENGTH 17 /* 17 bytes */
+
+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;
+ 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);
+
+ local_irq_save(flags);
+ 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;
+ }
+ }
+
+ local_irq_restore(flags);
+
+ return i;
+}
+
+/*
+ * guillemot_poll() reads and analyzes Guillemot joystick data.
+ */
+
+static void guillemot_poll(struct gameport *gameport)
+{
+ struct guillemot *guillemot = gameport_get_drvdata(gameport);
+ 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);
+ }
+
+ input_sync(dev);
+}
+
+/*
+ * guillemot_open() is a callback from the input open routine.
+ */
+
+static int guillemot_open(struct input_dev *dev)
+{
+ struct guillemot *guillemot = dev->private;
+
+ gameport_start_polling(guillemot->gameport);
+ 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;
+
+ gameport_stop_polling(guillemot->gameport);
+}
+
+/*
+ * guillemot_connect() probes for Guillemot joysticks.
+ */
+
+static int guillemot_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct guillemot *guillemot;
+ u8 data[GUILLEMOT_MAX_LENGTH];
+ int i, t;
+ int err;
+
+ if (!(guillemot = kcalloc(1, sizeof(struct guillemot), GFP_KERNEL)))
+ return -ENOMEM;
+
+ guillemot->gameport = gameport;
+
+ gameport_set_drvdata(gameport, guillemot);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ i = guillemot_read_packet(gameport, data);
+
+ if (i != GUILLEMOT_MAX_LENGTH * 8 || data[0] != 0x55 || data[16] != 0xaa) {
+ err = -ENODEV;
+ 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]);
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ gameport_set_poll_handler(gameport, guillemot_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ 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.id.bustype = BUS_GAMEPORT;
+ guillemot->dev.id.vendor = GAMEPORT_ID_VENDOR_GUILLEMOT;
+ guillemot->dev.id.product = guillemot_type[i].id;
+ guillemot->dev.id.version = (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++)
+ input_set_abs_params(&guillemot->dev, t, 0, 255, 0, 0);
+
+ if (guillemot->type->hat) {
+ input_set_abs_params(&guillemot->dev, ABS_HAT0X, -1, 1, 0, 0);
+ input_set_abs_params(&guillemot->dev, ABS_HAT0Y, -1, 1, 0, 0);
+ }
+
+ 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 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(guillemot);
+ return err;
+}
+
+static void guillemot_disconnect(struct gameport *gameport)
+{
+ struct guillemot *guillemot = gameport_get_drvdata(gameport);
+
+ 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_driver guillemot_drv = {
+ .driver = {
+ .name = "guillemot",
+ },
+ .description = DRIVER_DESC,
+ .connect = guillemot_connect,
+ .disconnect = guillemot_disconnect,
+};
+
+static int __init guillemot_init(void)
+{
+ gameport_register_driver(&guillemot_drv);
+ return 0;
+}
+
+static void __exit guillemot_exit(void)
+{
+ gameport_unregister_driver(&guillemot_drv);
+}
+
+module_init(guillemot_init);
+module_exit(guillemot_exit);
diff --git a/drivers/input/joystick/iforce/Kconfig b/drivers/input/joystick/iforce/Kconfig
new file mode 100644
index 000000000000..8fde22a021b3
--- /dev/null
+++ b/drivers/input/joystick/iforce/Kconfig
@@ -0,0 +1,32 @@
+#
+# I-Force driver configuration
+#
+config JOYSTICK_IFORCE
+ tristate "I-Force devices"
+ depends on INPUT && INPUT_JOYSTICK
+ help
+ Say Y here if you have an I-Force joystick or steering wheel
+
+ You also must choose at least one of the two options below.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iforce.
+
+config JOYSTICK_IFORCE_USB
+ bool "I-Force USB joysticks and wheels"
+ depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB
+ help
+ Say Y here if you have an I-Force joystick or steering wheel
+ connected to your USB port.
+
+config JOYSTICK_IFORCE_232
+ bool "I-Force Serial joysticks and wheels"
+ depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO
+ help
+ Say Y here if you have an I-Force joystick or steering wheel
+ connected to your serial (COM) port.
+
+ You will need an additional utility called inputattach, see
+ <file:Documentation/input/joystick.txt>
+ and <file:Documentation/input/ff.txt>.
+
diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile
new file mode 100644
index 000000000000..17ae42bf9ffd
--- /dev/null
+++ b/drivers/input/joystick/iforce/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the I-Force driver
+#
+# By Johann Deneux <deneux@ifrance.com>
+#
+
+# Goal definition
+iforce-objs := iforce-ff.o iforce-main.o iforce-packets.o
+
+obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
+ iforce-objs += iforce-serio.o
+endif
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
+ iforce-objs += iforce-usb.o
+endif
+
+EXTRA_CFLAGS = -Werror-implicit-function-declaration
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
new file mode 100644
index 000000000000..4678b6dab43b
--- /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..028f3513629a
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -0,0 +1,557 @@
+/*
+ * $Id: iforce-main.c,v 1.19 2002/07/07 10:22:50 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 CONFIG_JOYSTICK_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 CONFIG_JOYSTICK_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 CONFIG_JOYSTICK_IFORCE_USB
+ case IFORCE_USB:
+ iforce_usb_delete(iforce);
+ break;
+#endif
+#ifdef CONFIG_JOYSTICK_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.
+ */
+
+ switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+ case IFORCE_USB:
+ iforce->dev.id.bustype = BUS_USB;
+ iforce->dev.dev = &iforce->usbdev->dev;
+ break;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+ case IFORCE_232:
+ iforce->dev.id.bustype = BUS_RS232;
+ iforce->dev.dev = &iforce->serio->dev;
+ break;
+#endif
+ }
+
+ 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.id.vendor = (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.id.product = (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.id.vendor &&
+ iforce_device[i].idproduct == iforce->dev.id.product)
+ 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 CONFIG_JOYSTICK_IFORCE_USB
+ usb_register(&iforce_usb_driver);
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+ serio_register_driver(&iforce_serio_drv);
+#endif
+ return 0;
+}
+
+static void __exit iforce_exit(void)
+{
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+ usb_deregister(&iforce_usb_driver);
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+ serio_unregister_driver(&iforce_serio_drv);
+#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..58728ebaaf80
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-packets.c
@@ -0,0 +1,319 @@
+/*
+ * $Id: iforce-packets.c,v 1.16 2002/07/07 10:22:50 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 CONFIG_JOYSTICK_IFORCE_232
+ case IFORCE_232:
+ if (empty)
+ iforce_serial_xmit(iforce);
+ break;
+#endif
+#ifdef CONFIG_JOYSTICK_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 pt_regs *regs)
+{
+ 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 CONFIG_JOYSTICK_IFORCE_232
+ if (HI(iforce->expect_packet) == HI(cmd)) {
+ iforce->expect_packet = 0;
+ iforce->ecmd = cmd;
+ memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
+ wake_up(&iforce->wait);
+ }
+#endif
+
+ if (!iforce->type) {
+ being_used--;
+ return;
+ }
+
+ switch (HI(cmd)) {
+
+ case 0x01: /* joystick position data */
+ case 0x03: /* wheel position data */
+
+ input_regs(dev, regs);
+
+ 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);
+ }
+ }
+
+ input_sync(dev);
+
+ break;
+
+ case 0x02: /* status report */
+ input_regs(dev, regs);
+ input_report_key(dev, BTN_DEAD, data[0] & 0x02);
+ input_sync(dev);
+
+ /* 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 CONFIG_JOYSTICK_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_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;
+ }
+#else
+ printk(KERN_ERR "iforce_get_id_packet: iforce->bus = USB!\n");
+#endif
+ break;
+
+ case IFORCE_232:
+
+#ifdef CONFIG_JOYSTICK_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..11f51905cba7
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-serio.c
@@ -0,0 +1,193 @@
+/*
+ * $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)
+{
+ struct iforce *iforce = serio_get_drvdata(serio);
+
+ iforce_serial_xmit(iforce);
+}
+
+static irqreturn_t iforce_serio_irq(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct iforce *iforce = serio_get_drvdata(serio);
+
+ if (!iforce->pkt) {
+ if (data == 0x2b)
+ iforce->pkt = 1;
+ goto out;
+ }
+
+ if (!iforce->id) {
+ if (data > 3 && data != 0xff)
+ iforce->pkt = 0;
+ else
+ iforce->id = data;
+ goto out;
+ }
+
+ if (!iforce->len) {
+ if (data > IFORCE_MAX_LENGTH) {
+ iforce->pkt = 0;
+ iforce->id = 0;
+ } else {
+ iforce->len = data;
+ }
+ goto out;
+ }
+
+ if (iforce->idx < iforce->len) {
+ iforce->csum += iforce->data[iforce->idx++] = data;
+ goto out;
+ }
+
+ if (iforce->idx == iforce->len) {
+ iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data, regs);
+ iforce->pkt = 0;
+ iforce->id = 0;
+ iforce->len = 0;
+ iforce->idx = 0;
+ iforce->csum = 0;
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct iforce *iforce;
+ int err;
+
+ if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(iforce, 0, sizeof(struct iforce));
+
+ iforce->bus = IFORCE_232;
+ iforce->serio = serio;
+
+ serio_set_drvdata(serio, iforce);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(iforce);
+ return err;
+ }
+
+ if (iforce_init_device(iforce)) {
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(iforce);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void iforce_serio_disconnect(struct serio *serio)
+{
+ struct iforce *iforce = serio_get_drvdata(serio);
+
+ input_unregister_device(&iforce->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(iforce);
+}
+
+static struct serio_device_id iforce_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_IFORCE,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, iforce_serio_ids);
+
+struct serio_driver iforce_serio_drv = {
+ .driver = {
+ .name = "iforce",
+ },
+ .description = "RS232 I-Force joysticks and wheels driver",
+ .id_table = iforce_serio_ids,
+ .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..617c0b0e5a39
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -0,0 +1,243 @@
+ /*
+ * $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 pt_regs *regs)
+{
+ struct iforce *iforce = urb->context;
+ int status;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - urb has status of: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ iforce_process_packet(iforce,
+ (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1, regs);
+
+exit:
+ status = usb_submit_urb (urb, GFP_ATOMIC);
+ if (status)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, status);
+}
+
+static void iforce_usb_out(struct urb *urb, struct pt_regs *regs)
+{
+ 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);
+
+ wake_up(&iforce->wait);
+}
+
+static void iforce_usb_ctrl(struct urb *urb, struct pt_regs *regs)
+{
+ struct iforce *iforce = urb->context;
+ if (urb->status) return;
+ iforce->ecmd = 0xff00 | urb->actual_length;
+ wake_up(&iforce->wait);
+}
+
+static int iforce_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *epirq, *epout;
+ struct iforce *iforce;
+
+ interface = intf->cur_altsetting;
+
+ epirq = &interface->endpoint[0].desc;
+ epout = &interface->endpoint[1].desc;
+
+ 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;
+
+ usb_set_intfdata(intf, iforce);
+ return 0;
+
+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 -ENODEV;
+}
+
+/* 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_interface *intf)
+{
+ struct iforce *iforce = usb_get_intfdata(intf);
+ int open = 0; /* FIXME! iforce->dev.handle->open; */
+
+ usb_set_intfdata(intf, NULL);
+ if (iforce) {
+ 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..bce247bc300b
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce.h
@@ -0,0 +1,191 @@
+/*
+ * $Id: iforce.h,v 1.13 2002/07/07 10:22:50 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>
+
+/* 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
+
+/* iforce::bus */
+#define IFORCE_232 1
+#define IFORCE_USB 2
+
+#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 CONFIG_JOYSTICK_IFORCE_232
+ struct serio *serio; /* RS232 transfer */
+ int idx, pkt, len, id;
+ unsigned char csum;
+#endif
+#ifdef CONFIG_JOYSTICK_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, struct pt_regs *regs);
+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_driver iforce_serio_drv;
+extern struct usb_driver iforce_usb_driver;
diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c
new file mode 100644
index 000000000000..9d3f8c38cb09
--- /dev/null
+++ b/drivers/input/joystick/interact.c
@@ -0,0 +1,322 @@
+/*
+ * $Id: interact.c,v 1.16 2002/01/22 20:28:25 vojtech Exp $
+ *
+ * Copyright (c) 2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Toby Deshane
+ */
+
+/*
+ * InterAct digital gamepad/joystick 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>
+
+#define DRIVER_DESC "InterAct digital joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define INTERACT_MAX_START 600 /* 400 us */
+#define INTERACT_MAX_STROBE 60 /* 40 us */
+#define INTERACT_MAX_LENGTH 32 /* 32 bits */
+
+#define INTERACT_TYPE_HHFX 0 /* HammerHead/FX */
+#define INTERACT_TYPE_PP8D 1 /* ProPad 8 */
+
+struct interact {
+ struct gameport *gameport;
+ struct input_dev dev;
+ int bads;
+ int reads;
+ unsigned char type;
+ unsigned char length;
+ char phys[32];
+};
+
+static short interact_abs_hhfx[] =
+ { ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 };
+static short interact_abs_pp8d[] =
+ { ABS_X, ABS_Y, -1 };
+
+static short interact_btn_hhfx[] =
+ { BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL, BTN_TL2, BTN_TR2, BTN_MODE, BTN_SELECT, -1 };
+static short interact_btn_pp8d[] =
+ { BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 };
+
+struct interact_type {
+ int id;
+ short *abs;
+ short *btn;
+ char *name;
+ unsigned char length;
+ unsigned char b8;
+};
+
+static struct interact_type interact_type[] = {
+ { 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX", 32, 4 },
+ { 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 },
+ { 0 }};
+
+/*
+ * interact_read_packet() reads and InterAct joystick data.
+ */
+
+static int interact_read_packet(struct gameport *gameport, int length, u32 *data)
+{
+ unsigned long flags;
+ unsigned char u, v;
+ unsigned int t, s;
+ int i;
+
+ i = 0;
+ data[0] = data[1] = data[2] = 0;
+ t = gameport_time(gameport, INTERACT_MAX_START);
+ s = gameport_time(gameport, INTERACT_MAX_STROBE);
+
+ local_irq_save(flags);
+ gameport_trigger(gameport);
+ v = gameport_read(gameport);
+
+ while (t > 0 && i < length) {
+ t--;
+ u = v; v = gameport_read(gameport);
+ if (v & ~u & 0x40) {
+ data[0] = (data[0] << 1) | ((v >> 4) & 1);
+ data[1] = (data[1] << 1) | ((v >> 5) & 1);
+ data[2] = (data[2] << 1) | ((v >> 7) & 1);
+ i++;
+ t = s;
+ }
+ }
+
+ local_irq_restore(flags);
+
+ return i;
+}
+
+/*
+ * interact_poll() reads and analyzes InterAct joystick data.
+ */
+
+static void interact_poll(struct gameport *gameport)
+{
+ struct interact *interact = gameport_get_drvdata(gameport);
+ struct input_dev *dev = &interact->dev;
+ u32 data[3];
+ int i;
+
+ interact->reads++;
+
+ if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) {
+ interact->bads++;
+ } else {
+
+ for (i = 0; i < 3; i++)
+ data[i] <<= INTERACT_MAX_LENGTH - interact->length;
+
+ switch (interact->type) {
+
+ case INTERACT_TYPE_HHFX:
+
+ for (i = 0; i < 4; i++)
+ input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff);
+
+ for (i = 0; i < 2; i++)
+ input_report_abs(dev, ABS_HAT0Y - i,
+ ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1));
+
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);
+
+ for (i = 0; i < 4; i++)
+ input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
+
+ break;
+
+ case INTERACT_TYPE_PP8D:
+
+ for (i = 0; i < 2; i++)
+ input_report_abs(dev, interact_abs_pp8d[i],
+ ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1));
+
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
+
+ break;
+ }
+ }
+
+ input_sync(dev);
+}
+
+/*
+ * interact_open() is a callback from the input open routine.
+ */
+
+static int interact_open(struct input_dev *dev)
+{
+ struct interact *interact = dev->private;
+
+ gameport_start_polling(interact->gameport);
+ return 0;
+}
+
+/*
+ * interact_close() is a callback from the input close routine.
+ */
+
+static void interact_close(struct input_dev *dev)
+{
+ struct interact *interact = dev->private;
+
+ gameport_stop_polling(interact->gameport);
+}
+
+/*
+ * interact_connect() probes for InterAct joysticks.
+ */
+
+static int interact_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct interact *interact;
+ __u32 data[3];
+ int i, t;
+ int err;
+
+ if (!(interact = kcalloc(1, sizeof(struct interact), GFP_KERNEL)))
+ return -ENOMEM;
+
+ interact->gameport = gameport;
+
+ gameport_set_drvdata(gameport, interact);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data);
+
+ if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ for (i = 0; interact_type[i].length; i++)
+ if (interact_type[i].id == (data[2] >> 16))
+ break;
+
+ if (!interact_type[i].length) {
+ printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n",
+ gameport->phys, i, data[0], data[1], data[2]);
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ gameport_set_poll_handler(gameport, interact_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ sprintf(interact->phys, "%s/input0", gameport->phys);
+
+ interact->type = i;
+ interact->length = interact_type[i].length;
+
+ interact->dev.private = interact;
+ interact->dev.open = interact_open;
+ interact->dev.close = interact_close;
+
+ interact->dev.name = interact_type[i].name;
+ interact->dev.phys = interact->phys;
+ interact->dev.id.bustype = BUS_GAMEPORT;
+ interact->dev.id.vendor = GAMEPORT_ID_VENDOR_INTERACT;
+ interact->dev.id.product = interact_type[i].id;
+ interact->dev.id.version = 0x0100;
+
+ interact->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
+ set_bit(t, interact->dev.absbit);
+ if (i < interact_type[interact->type].b8) {
+ interact->dev.absmin[t] = 0;
+ interact->dev.absmax[t] = 255;
+ } else {
+ interact->dev.absmin[t] = -1;
+ interact->dev.absmax[t] = 1;
+ }
+ }
+
+ for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
+ set_bit(t, interact->dev.keybit);
+
+ input_register_device(&interact->dev);
+ printk(KERN_INFO "input: %s on %s\n",
+ interact_type[interact->type].name, gameport->phys);
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(interact);
+ return err;
+}
+
+static void interact_disconnect(struct gameport *gameport)
+{
+ struct interact *interact = gameport_get_drvdata(gameport);
+
+ input_unregister_device(&interact->dev);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(interact);
+}
+
+static struct gameport_driver interact_drv = {
+ .driver = {
+ .name = "interact",
+ },
+ .description = DRIVER_DESC,
+ .connect = interact_connect,
+ .disconnect = interact_disconnect,
+};
+
+static int __init interact_init(void)
+{
+ gameport_register_driver(&interact_drv);
+ return 0;
+}
+
+static void __exit interact_exit(void)
+{
+ gameport_unregister_driver(&interact_drv);
+}
+
+module_init(interact_init);
+module_exit(interact_exit);
diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c
new file mode 100644
index 000000000000..4234ccaf9146
--- /dev/null
+++ b/drivers/input/joystick/joydump.c
@@ -0,0 +1,175 @@
+/*
+ * $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>
+
+#define DRIVER_DESC "Gameport data dumper module"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define BUF_SIZE 256
+
+struct joydump {
+ unsigned int time;
+ unsigned char data;
+};
+
+static int joydump_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct joydump *buf; /* all entries */
+ struct joydump *dump, *prev; /* one entry each */
+ 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: %30s |\n", gameport->phys);
+ printk(KERN_INFO "joydump: | Speed: %28d kHz |\n", gameport->speed);
+
+ if (gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) {
+
+ printk(KERN_INFO "joydump: | Raw mode not available - trying cooked. |\n");
+
+ if (gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) {
+
+ printk(KERN_INFO "joydump: | Cooked not available either. Failing. |\n");
+ printk(KERN_INFO "joydump: `------------------- END -----------------'\n");
+ return -ENODEV;
+ }
+
+ 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 */
+
+ buf = kmalloc(BUF_SIZE * sizeof(struct joydump), GFP_KERNEL);
+ if (!buf) {
+ printk(KERN_INFO "joydump: no memory for testing\n");
+ goto jd_end;
+ }
+ dump = buf;
+ t = 0;
+ i = 1;
+
+ local_irq_save(flags);
+
+ u = gameport_read(gameport);
+
+ dump->data = u;
+ dump->time = t;
+ dump++;
+
+ gameport_trigger(gameport);
+
+ while (i < BUF_SIZE && t < timeout) {
+
+ dump->data = gameport_read(gameport);
+
+ if (dump->data ^ u) {
+ u = dump->data;
+ dump->time = t;
+ i++;
+ dump++;
+ }
+ t++;
+ }
+
+ local_irq_restore(flags);
+
+/*
+ * Dump data.
+ */
+
+ t = i;
+ dump = buf;
+ prev = dump;
+
+ printk(KERN_INFO "joydump: >------------------ DATA -----------------<\n");
+ printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ", 0, 0);
+ for (j = 7; j >= 0; j--)
+ printk("%d", (dump->data >> j) & 1);
+ printk(" |\n");
+ dump++;
+
+ for (i = 1; i < t; i++, dump++, prev++) {
+ printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ",
+ i, dump->time - prev->time);
+ for (j = 7; j >= 0; j--)
+ printk("%d", (dump->data >> j) & 1);
+ printk(" |\n");
+ }
+ kfree(buf);
+
+jd_end:
+ printk(KERN_INFO "joydump: `------------------- END -----------------'\n");
+
+ return 0;
+}
+
+static void joydump_disconnect(struct gameport *gameport)
+{
+ gameport_close(gameport);
+}
+
+static struct gameport_driver joydump_drv = {
+ .driver = {
+ .name = "joydump",
+ },
+ .description = DRIVER_DESC,
+ .connect = joydump_connect,
+ .disconnect = joydump_disconnect,
+};
+
+static int __init joydump_init(void)
+{
+ gameport_register_driver(&joydump_drv);
+ return 0;
+}
+
+static void __exit joydump_exit(void)
+{
+ gameport_unregister_driver(&joydump_drv);
+}
+
+module_init(joydump_init);
+module_exit(joydump_exit);
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
new file mode 100644
index 000000000000..1ba503627242
--- /dev/null
+++ b/drivers/input/joystick/magellan.c
@@ -0,0 +1,247 @@
+/*
+ * $Id: magellan.c,v 1.16 2002/01/22 20:28:39 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Magellan and Space Mouse 6dof 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Magellan and SpaceMouse 6dof controller driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define MAGELLAN_MAX_LENGTH 32
+
+static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 };
+static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+static char *magellan_name = "LogiCad3D Magellan / SpaceMouse";
+
+/*
+ * Per-Magellan data.
+ */
+
+struct magellan {
+ struct input_dev dev;
+ int idx;
+ unsigned char data[MAGELLAN_MAX_LENGTH];
+ char phys[32];
+};
+
+/*
+ * magellan_crunch_nibbles() verifies that the bytes sent from the Magellan
+ * have correct upper nibbles for the lower ones, if not, the packet will
+ * be thrown away. It also strips these upper halves to simplify further
+ * processing.
+ */
+
+static int magellan_crunch_nibbles(unsigned char *data, int count)
+{
+ static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?";
+
+ do {
+ if (data[count] == nibbles[data[count] & 0xf])
+ data[count] = data[count] & 0xf;
+ else
+ return -1;
+ } while (--count);
+
+ return 0;
+}
+
+static void magellan_process_packet(struct magellan* magellan, struct pt_regs *regs)
+{
+ struct input_dev *dev = &magellan->dev;
+ unsigned char *data = magellan->data;
+ int i, t;
+
+ if (!magellan->idx) return;
+
+ input_regs(dev, regs);
+
+ switch (magellan->data[0]) {
+
+ case 'd': /* Axis data */
+ if (magellan->idx != 25) return;
+ if (magellan_crunch_nibbles(data, 24)) return;
+ for (i = 0; i < 6; i++)
+ input_report_abs(dev, magellan_axes[i],
+ (data[(i << 2) + 1] << 12 | data[(i << 2) + 2] << 8 |
+ data[(i << 2) + 3] << 4 | data[(i << 2) + 4]) - 32768);
+ break;
+
+ case 'k': /* Button data */
+ if (magellan->idx != 4) return;
+ if (magellan_crunch_nibbles(data, 3)) return;
+ t = (data[1] << 1) | (data[2] << 5) | data[3];
+ for (i = 0; i < 9; i++) input_report_key(dev, magellan_buttons[i], (t >> i) & 1);
+ break;
+ }
+
+ input_sync(dev);
+}
+
+static irqreturn_t magellan_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct magellan* magellan = serio_get_drvdata(serio);
+
+ if (data == '\r') {
+ magellan_process_packet(magellan, regs);
+ magellan->idx = 0;
+ } else {
+ if (magellan->idx < MAGELLAN_MAX_LENGTH)
+ magellan->data[magellan->idx++] = data;
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * magellan_disconnect() is the opposite of magellan_connect()
+ */
+
+static void magellan_disconnect(struct serio *serio)
+{
+ struct magellan* magellan = serio_get_drvdata(serio);
+
+ input_unregister_device(&magellan->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(magellan);
+}
+
+/*
+ * magellan_connect() is the routine that is called when someone adds a
+ * new serio device that supports Magellan protocol and registers it as
+ * an input device.
+ */
+
+static int magellan_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct magellan *magellan;
+ int i, t;
+ int err;
+
+ if (!(magellan = kmalloc(sizeof(struct magellan), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(magellan, 0, sizeof(struct magellan));
+
+ magellan->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (i = 0; i < 9; i++)
+ set_bit(magellan_buttons[i], magellan->dev.keybit);
+
+ for (i = 0; i < 6; i++) {
+ t = magellan_axes[i];
+ set_bit(t, magellan->dev.absbit);
+ magellan->dev.absmin[t] = -360;
+ magellan->dev.absmax[t] = 360;
+ }
+
+ sprintf(magellan->phys, "%s/input0", serio->phys);
+
+ init_input_dev(&magellan->dev);
+ magellan->dev.private = magellan;
+ magellan->dev.name = magellan_name;
+ magellan->dev.phys = magellan->phys;
+ magellan->dev.id.bustype = BUS_RS232;
+ magellan->dev.id.vendor = SERIO_MAGELLAN;
+ magellan->dev.id.product = 0x0001;
+ magellan->dev.id.version = 0x0100;
+ magellan->dev.dev = &serio->dev;
+
+ serio_set_drvdata(serio, magellan);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(magellan);
+ return err;
+ }
+
+ input_register_device(&magellan->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", magellan_name, serio->phys);
+
+ return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id magellan_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MAGELLAN,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, magellan_serio_ids);
+
+static struct serio_driver magellan_drv = {
+ .driver = {
+ .name = "magellan",
+ },
+ .description = DRIVER_DESC,
+ .id_table = magellan_serio_ids,
+ .interrupt = magellan_interrupt,
+ .connect = magellan_connect,
+ .disconnect = magellan_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init magellan_init(void)
+{
+ serio_register_driver(&magellan_drv);
+ return 0;
+}
+
+static void __exit magellan_exit(void)
+{
+ serio_unregister_driver(&magellan_drv);
+}
+
+module_init(magellan_init);
+module_exit(magellan_exit);
diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c
new file mode 100644
index 000000000000..47144a7ed9e7
--- /dev/null
+++ b/drivers/input/joystick/sidewinder.c
@@ -0,0 +1,807 @@
+/*
+ * Copyright (c) 1998-2005 Vojtech Pavlik
+ */
+
+/*
+ * Microsoft SideWinder joystick family 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+
+#define DRIVER_DESC "Microsoft SideWinder joystick family driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * These are really magic values. Changing them can make a problem go away,
+ * as well as break everything.
+ */
+
+#undef SW_DEBUG
+#undef SW_DEBUG_DATA
+
+#define SW_START 600 /* The time we wait for the first bit [600 us] */
+#define SW_STROBE 60 /* Max time per bit [60 us] */
+#define SW_TIMEOUT 6 /* Wait for everything to settle [6 ms] */
+#define SW_KICK 45 /* Wait after A0 fall till kick [45 us] */
+#define SW_END 8 /* Number of bits before end of packet to kick */
+#define SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */
+#define SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */
+#define SW_OK 64 /* Number of packet read successes to switch optimization back on */
+#define SW_LENGTH 512 /* Max number of bits in a packet */
+
+#ifdef SW_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+/*
+ * SideWinder joystick types ...
+ */
+
+#define SW_ID_3DP 0
+#define SW_ID_GP 1
+#define SW_ID_PP 2
+#define SW_ID_FFP 3
+#define SW_ID_FSP 4
+#define SW_ID_FFW 5
+
+/*
+ * Names, buttons, axes ...
+ */
+
+static char *sw_name[] = { "3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro",
+ "Force Feedback Wheel" };
+
+static char sw_abs[][7] = {
+ { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+ { ABS_X, ABS_Y },
+ { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+ { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+ { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+ { ABS_RX, ABS_RUDDER, ABS_THROTTLE }};
+
+static char sw_bit[][7] = {
+ { 10, 10, 9, 10, 1, 1 },
+ { 1, 1 },
+ { 10, 10, 6, 7, 1, 1 },
+ { 10, 10, 6, 7, 1, 1 },
+ { 10, 10, 6, 1, 1 },
+ { 10, 7, 7, 1, 1 }};
+
+static short sw_btn[][12] = {
+ { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE },
+ { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE },
+ { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },
+ { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },
+ { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT },
+ { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }};
+
+static struct {
+ int x;
+ int y;
+} sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct sw {
+ struct gameport *gameport;
+ struct input_dev dev[4];
+ char name[64];
+ char phys[4][32];
+ int length;
+ int type;
+ int bits;
+ int number;
+ int fail;
+ int ok;
+ int reads;
+ int bads;
+};
+
+/*
+ * sw_read_packet() is a function which reads either a data packet, or an
+ * identification packet from a SideWinder joystick. The protocol is very,
+ * very, very braindamaged. Microsoft patented it in US patent #5628686.
+ */
+
+static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id)
+{
+ unsigned long flags;
+ int timeout, bitout, sched, i, kick, start, strobe;
+ unsigned char pending, u, v;
+
+ i = -id; /* Don't care about data, only want ID */
+ timeout = id ? gameport_time(gameport, SW_TIMEOUT * 1000) : 0; /* Set up global timeout for ID packet */
+ kick = id ? gameport_time(gameport, SW_KICK) : 0; /* Set up kick timeout for ID packet */
+ start = gameport_time(gameport, SW_START);
+ strobe = gameport_time(gameport, SW_STROBE);
+ bitout = start;
+ pending = 0;
+ sched = 0;
+
+ local_irq_save(flags); /* Quiet, please */
+
+ gameport_trigger(gameport); /* Trigger */
+ v = gameport_read(gameport);
+
+ do {
+ bitout--;
+ u = v;
+ v = gameport_read(gameport);
+ } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */
+
+ if (bitout > 0)
+ bitout = strobe; /* Extend time if not timed out */
+
+ while ((timeout > 0 || bitout > 0) && (i < length)) {
+
+ timeout--;
+ bitout--; /* Decrement timers */
+ sched--;
+
+ u = v;
+ v = gameport_read(gameport);
+
+ if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */
+ if (i >= 0) /* Want this data */
+ buf[i] = v >> 5; /* Store it */
+ i++; /* Advance index */
+ bitout = strobe; /* Extend timeout for next bit */
+ }
+
+ if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */
+ sched = kick; /* Schedule second trigger */
+ kick = 0; /* Don't schedule next time on falling edge */
+ pending = 1; /* Mark schedule */
+ }
+
+ if (pending && sched < 0 && (i > -SW_END)) { /* Second trigger time */
+ gameport_trigger(gameport); /* Trigger */
+ bitout = start; /* Long bit timeout */
+ pending = 0; /* Unmark schedule */
+ timeout = 0; /* Switch from global to bit timeouts */
+ }
+ }
+
+ local_irq_restore(flags); /* Done - relax */
+
+#ifdef SW_DEBUG_DATA
+ {
+ int j;
+ printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i);
+ for (j = 0; j < i; j++) printk("%d", buf[j]);
+ printk("]\n");
+ }
+#endif
+
+ return i;
+}
+
+/*
+ * sw_get_bits() and GB() compose bits from the triplet buffer into a __u64.
+ * Parameter 'pos' is bit number inside packet where to start at, 'num' is number
+ * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits
+ * is number of bits per triplet.
+ */
+
+#define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits)
+
+static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits)
+{
+ __u64 data = 0;
+ int tri = pos % bits; /* Start position */
+ int i = pos / bits;
+ int bit = 0;
+
+ while (num--) {
+ data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */
+ if (tri == bits) {
+ i++; /* Next triplet */
+ tri = 0;
+ }
+ }
+
+ return data;
+}
+
+/*
+ * sw_init_digital() initializes a SideWinder 3D Pro joystick
+ * into digital mode.
+ */
+
+static void sw_init_digital(struct gameport *gameport)
+{
+ int seq[] = { 140, 140+725, 140+300, 0 };
+ unsigned long flags;
+ int i, t;
+
+ local_irq_save(flags);
+
+ i = 0;
+ do {
+ gameport_trigger(gameport); /* Trigger */
+ t = gameport_time(gameport, SW_TIMEOUT * 1000);
+ while ((gameport_read(gameport) & 1) && t) t--; /* Wait for axis to fall back to 0 */
+ udelay(seq[i]); /* Delay magic time */
+ } while (seq[++i]);
+
+ gameport_trigger(gameport); /* Last trigger */
+
+ local_irq_restore(flags);
+}
+
+/*
+ * sw_parity() computes parity of __u64
+ */
+
+static int sw_parity(__u64 t)
+{
+ int x = t ^ (t >> 32);
+
+ x ^= x >> 16;
+ x ^= x >> 8;
+ x ^= x >> 4;
+ x ^= x >> 2;
+ x ^= x >> 1;
+ return x & 1;
+}
+
+/*
+ * sw_ccheck() checks synchronization bits and computes checksum of nibbles.
+ */
+
+static int sw_check(__u64 t)
+{
+ unsigned char sum = 0;
+
+ if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */
+ return -1;
+
+ while (t) { /* Sum */
+ sum += t & 0xf;
+ t >>= 4;
+ }
+
+ return sum & 0xf;
+}
+
+/*
+ * sw_parse() analyzes SideWinder joystick data, and writes the results into
+ * the axes and buttons arrays.
+ */
+
+static int sw_parse(unsigned char *buf, struct sw *sw)
+{
+ int hat, i, j;
+ struct input_dev *dev = sw->dev;
+
+ switch (sw->type) {
+
+ case SW_ID_3DP:
+
+ if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8)
+ return -1;
+
+ input_report_abs(dev, ABS_X, (GB( 3,3) << 7) | GB(16,7));
+ input_report_abs(dev, ABS_Y, (GB( 0,3) << 7) | GB(24,7));
+ input_report_abs(dev, ABS_RZ, (GB(35,2) << 7) | GB(40,7));
+ input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7));
+
+ input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+ input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+ for (j = 0; j < 7; j++)
+ input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1));
+
+ input_report_key(dev, BTN_BASE4, !GB(38,1));
+ input_report_key(dev, BTN_BASE5, !GB(37,1));
+
+ input_sync(dev);
+
+ return 0;
+
+ case SW_ID_GP:
+
+ for (i = 0; i < sw->number; i ++) {
+
+ if (sw_parity(GB(i*15,15)))
+ return -1;
+
+ input_report_abs(dev + i, ABS_X, GB(i*15+3,1) - GB(i*15+2,1));
+ input_report_abs(dev + i, ABS_Y, GB(i*15+0,1) - GB(i*15+1,1));
+
+ for (j = 0; j < 10; j++)
+ input_report_key(dev + i, sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1));
+
+ input_sync(dev + i);
+ }
+
+ return 0;
+
+ case SW_ID_PP:
+ case SW_ID_FFP:
+
+ if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8)
+ return -1;
+
+ input_report_abs(dev, ABS_X, GB( 9,10));
+ input_report_abs(dev, ABS_Y, GB(19,10));
+ input_report_abs(dev, ABS_RZ, GB(36, 6));
+ input_report_abs(dev, ABS_THROTTLE, GB(29, 7));
+
+ input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+ input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+ for (j = 0; j < 9; j++)
+ input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1));
+
+ input_sync(dev);
+
+ return 0;
+
+ case SW_ID_FSP:
+
+ if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8)
+ return -1;
+
+ input_report_abs(dev, ABS_X, GB( 0,10));
+ input_report_abs(dev, ABS_Y, GB(16,10));
+ input_report_abs(dev, ABS_THROTTLE, GB(32, 6));
+
+ input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+ input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+ for (j = 0; j < 6; j++)
+ input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1));
+
+ input_report_key(dev, BTN_TR, !GB(26,1));
+ input_report_key(dev, BTN_START, !GB(27,1));
+ input_report_key(dev, BTN_MODE, !GB(38,1));
+ input_report_key(dev, BTN_SELECT, !GB(39,1));
+
+ input_sync(dev);
+
+ return 0;
+
+ case SW_ID_FFW:
+
+ if (!sw_parity(GB(0,33)))
+ return -1;
+
+ input_report_abs(dev, ABS_RX, GB( 0,10));
+ input_report_abs(dev, ABS_RUDDER, GB(10, 6));
+ input_report_abs(dev, ABS_THROTTLE, GB(16, 6));
+
+ for (j = 0; j < 8; j++)
+ input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1));
+
+ input_sync(dev);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * sw_read() reads SideWinder joystick data, and reinitializes
+ * the joystick in case of persistent problems. This is the function that is
+ * called from the generic code to poll the joystick.
+ */
+
+static int sw_read(struct sw *sw)
+{
+ unsigned char buf[SW_LENGTH];
+ int i;
+
+ i = sw_read_packet(sw->gameport, buf, sw->length, 0);
+
+ if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) { /* Broken packet, try to fix */
+
+ if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) { /* Last init failed, 1 bit mode */
+ printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on %s"
+ " - going to reinitialize.\n", sw->gameport->phys);
+ sw->fail = SW_FAIL; /* Reinitialize */
+ i = 128; /* Bogus value */
+ }
+
+ if (i < 66 && GB(0,64) == GB(i*3-66,64)) /* 1 == 3 */
+ i = 66; /* Everything is fine */
+
+ if (i < 66 && GB(0,64) == GB(66,64)) /* 1 == 2 */
+ i = 66; /* Everything is fine */
+
+ if (i < 66 && GB(i*3-132,64) == GB(i*3-66,64)) { /* 2 == 3 */
+ memmove(buf, buf + i - 22, 22); /* Move data */
+ i = 66; /* Carry on */
+ }
+ }
+
+ if (i == sw->length && !sw_parse(buf, sw)) { /* Parse data */
+
+ sw->fail = 0;
+ sw->ok++;
+
+ if (sw->type == SW_ID_3DP && sw->length == 66 /* Many packets OK */
+ && sw->ok > SW_OK) {
+
+ printk(KERN_INFO "sidewinder.c: No more trouble on %s"
+ " - enabling optimization again.\n", sw->gameport->phys);
+ sw->length = 22;
+ }
+
+ return 0;
+ }
+
+ sw->ok = 0;
+ sw->fail++;
+
+ if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) { /* Consecutive bad packets */
+
+ printk(KERN_INFO "sidewinder.c: Many bit errors on %s"
+ " - disabling optimization.\n", sw->gameport->phys);
+ sw->length = 66;
+ }
+
+ if (sw->fail < SW_FAIL)
+ return -1; /* Not enough, don't reinitialize yet */
+
+ printk(KERN_WARNING "sidewinder.c: Too many bit errors on %s"
+ " - reinitializing joystick.\n", sw->gameport->phys);
+
+ if (!i && sw->type == SW_ID_3DP) { /* 3D Pro can be in analog mode */
+ mdelay(3 * SW_TIMEOUT);
+ sw_init_digital(sw->gameport);
+ }
+
+ mdelay(SW_TIMEOUT);
+ i = sw_read_packet(sw->gameport, buf, SW_LENGTH, 0); /* Read normal data packet */
+ mdelay(SW_TIMEOUT);
+ sw_read_packet(sw->gameport, buf, SW_LENGTH, i); /* Read ID packet, this initializes the stick */
+
+ sw->fail = SW_FAIL;
+
+ return -1;
+}
+
+static void sw_poll(struct gameport *gameport)
+{
+ struct sw *sw = gameport_get_drvdata(gameport);
+
+ sw->reads++;
+ if (sw_read(sw))
+ sw->bads++;
+}
+
+static int sw_open(struct input_dev *dev)
+{
+ struct sw *sw = dev->private;
+
+ gameport_start_polling(sw->gameport);
+ return 0;
+}
+
+static void sw_close(struct input_dev *dev)
+{
+ struct sw *sw = dev->private;
+
+ gameport_stop_polling(sw->gameport);
+}
+
+/*
+ * sw_print_packet() prints the contents of a SideWinder packet.
+ */
+
+static void sw_print_packet(char *name, int length, unsigned char *buf, char bits)
+{
+ int i;
+
+ printk(KERN_INFO "sidewinder.c: %s packet, %d bits. [", name, length);
+ for (i = (((length + 3) >> 2) - 1); i >= 0; i--)
+ printk("%x", (int)sw_get_bits(buf, i << 2, 4, bits));
+ printk("]\n");
+}
+
+/*
+ * sw_3dp_id() translates the 3DP id into a human legible string.
+ * Unfortunately I don't know how to do this for the other SW types.
+ */
+
+static void sw_3dp_id(unsigned char *buf, char *comment)
+{
+ int i;
+ char pnp[8], rev[9];
+
+ for (i = 0; i < 7; i++) /* ASCII PnP ID */
+ pnp[i] = sw_get_bits(buf, 24+8*i, 8, 1);
+
+ for (i = 0; i < 8; i++) /* ASCII firmware revision */
+ rev[i] = sw_get_bits(buf, 88+8*i, 8, 1);
+
+ pnp[7] = rev[8] = 0;
+
+ sprintf(comment, " [PnP %d.%02d id %s rev %s]",
+ (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | /* Two 6-bit values */
+ sw_get_bits(buf, 16, 6, 1)) / 100,
+ (int) ((sw_get_bits(buf, 8, 6, 1) << 6) |
+ sw_get_bits(buf, 16, 6, 1)) % 100,
+ pnp, rev);
+}
+
+/*
+ * sw_guess_mode() checks the upper two button bits for toggling -
+ * indication of that the joystick is in 3-bit mode. This is documented
+ * behavior for 3DP ID packet, and for example the FSP does this in
+ * normal packets instead. Fun ...
+ */
+
+static int sw_guess_mode(unsigned char *buf, int len)
+{
+ int i;
+ unsigned char xor = 0;
+
+ for (i = 1; i < len; i++)
+ xor |= (buf[i - 1] ^ buf[i]) & 6;
+
+ return !!xor * 2 + 1;
+}
+
+/*
+ * sw_connect() probes for SideWinder type joysticks.
+ */
+
+static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct sw *sw;
+ int i, j, k, l;
+ int err;
+ unsigned char *buf = NULL; /* [SW_LENGTH] */
+ unsigned char *idbuf = NULL; /* [SW_LENGTH] */
+ unsigned char m = 1;
+ char comment[40];
+
+ comment[0] = 0;
+
+ sw = kcalloc(1, sizeof(struct sw), GFP_KERNEL);
+ buf = kmalloc(SW_LENGTH, GFP_KERNEL);
+ idbuf = kmalloc(SW_LENGTH, GFP_KERNEL);
+ if (!sw || !buf || !idbuf) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ sw->gameport = gameport;
+
+ gameport_set_drvdata(gameport, sw);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ dbg("Init 0: Opened %s, io %#x, speed %d",
+ gameport->phys, gameport->io, gameport->speed);
+
+ i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read normal packet */
+ msleep(SW_TIMEOUT);
+ dbg("Init 1: Mode %d. Length %d.", m , i);
+
+ if (!i) { /* No data. 3d Pro analog mode? */
+ sw_init_digital(gameport); /* Switch to digital */
+ msleep(SW_TIMEOUT);
+ i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */
+ msleep(SW_TIMEOUT);
+ dbg("Init 1b: Length %d.", i);
+ if (!i) { /* No data -> FAIL */
+ err = -ENODEV;
+ goto fail2;
+ }
+ }
+
+ j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Read ID. This initializes the stick */
+ m |= sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */
+ dbg("Init 2: Mode %d. ID Length %d.", m, j);
+
+ if (j <= 0) { /* Read ID failed. Happens in 1-bit mode on PP */
+ msleep(SW_TIMEOUT);
+ i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */
+ m |= sw_guess_mode(buf, i);
+ dbg("Init 2b: Mode %d. Length %d.", m, i);
+ if (!i) {
+ err = -ENODEV;
+ goto fail2;
+ }
+ msleep(SW_TIMEOUT);
+ j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Retry reading ID */
+ dbg("Init 2c: ID Length %d.", j);
+ }
+
+ sw->type = -1;
+ k = SW_FAIL; /* Try SW_FAIL times */
+ l = 0;
+
+ do {
+ k--;
+ msleep(SW_TIMEOUT);
+ i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read data packet */
+ dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k);
+
+ if (i > l) { /* Longer? As we can only lose bits, it makes */
+ /* no sense to try detection for a packet shorter */
+ l = i; /* than the previous one */
+
+ sw->number = 1;
+ sw->gameport = gameport;
+ sw->length = i;
+ sw->bits = m;
+
+ dbg("Init 3a: Case %d.\n", i * m);
+
+ switch (i * m) {
+ case 60:
+ sw->number++;
+ case 45: /* Ambiguous packet length */
+ if (j <= 40) { /* ID length less or eq 40 -> FSP */
+ case 43:
+ sw->type = SW_ID_FSP;
+ break;
+ }
+ sw->number++;
+ case 30:
+ sw->number++;
+ case 15:
+ sw->type = SW_ID_GP;
+ break;
+ case 33:
+ case 31:
+ sw->type = SW_ID_FFW;
+ break;
+ case 48: /* Ambiguous */
+ if (j == 14) { /* ID length 14*3 -> FFP */
+ sw->type = SW_ID_FFP;
+ sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on");
+ } else
+ sw->type = SW_ID_PP;
+ break;
+ case 66:
+ sw->bits = 3;
+ case 198:
+ sw->length = 22;
+ case 64:
+ sw->type = SW_ID_3DP;
+ if (j == 160) sw_3dp_id(idbuf, comment);
+ break;
+ }
+ }
+
+ } while (k && sw->type == -1);
+
+ if (sw->type == -1) {
+ printk(KERN_WARNING "sidewinder.c: unknown joystick device detected "
+ "on %s, contact <vojtech@ucw.cz>\n", gameport->phys);
+ sw_print_packet("ID", j * 3, idbuf, 3);
+ sw_print_packet("Data", i * m, buf, m);
+ err = -ENODEV;
+ goto fail2;
+ }
+
+#ifdef SW_DEBUG
+ sw_print_packet("ID", j * 3, idbuf, 3);
+ sw_print_packet("Data", i * m, buf, m);
+#endif
+
+ gameport_set_poll_handler(gameport, sw_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ k = i;
+ l = j;
+
+ for (i = 0; i < sw->number; i++) {
+ int bits, code;
+
+ sprintf(sw->name, "Microsoft SideWinder %s", sw_name[sw->type]);
+ sprintf(sw->phys[i], "%s/input%d", gameport->phys, i);
+
+ sw->dev[i].private = sw;
+
+ sw->dev[i].open = sw_open;
+ sw->dev[i].close = sw_close;
+
+ sw->dev[i].name = sw->name;
+ sw->dev[i].phys = sw->phys[i];
+ sw->dev[i].id.bustype = BUS_GAMEPORT;
+ sw->dev[i].id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT;
+ sw->dev[i].id.product = sw->type;
+ sw->dev[i].id.version = 0x0100;
+
+ sw->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (j = 0; (bits = sw_bit[sw->type][j]); j++) {
+ code = sw_abs[sw->type][j];
+ set_bit(code, sw->dev[i].absbit);
+ sw->dev[i].absmax[code] = (1 << bits) - 1;
+ sw->dev[i].absmin[code] = (bits == 1) ? -1 : 0;
+ sw->dev[i].absfuzz[code] = ((bits >> 1) >= 2) ? (1 << ((bits >> 1) - 2)) : 0;
+ if (code != ABS_THROTTLE)
+ sw->dev[i].absflat[code] = (bits >= 5) ? (1 << (bits - 5)) : 0;
+ }
+
+ for (j = 0; (code = sw_btn[sw->type][j]); j++)
+ set_bit(code, sw->dev[i].keybit);
+
+ input_register_device(sw->dev + i);
+ printk(KERN_INFO "input: %s%s on %s [%d-bit id %d data %d]\n",
+ sw->name, comment, gameport->phys, m, l, k);
+ }
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(sw);
+ kfree(buf);
+ kfree(idbuf);
+ return err;
+}
+
+static void sw_disconnect(struct gameport *gameport)
+{
+ struct sw *sw = gameport_get_drvdata(gameport);
+ int i;
+
+ for (i = 0; i < sw->number; i++)
+ input_unregister_device(sw->dev + i);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(sw);
+}
+
+static struct gameport_driver sw_drv = {
+ .driver = {
+ .name = "sidewinder",
+ },
+ .description = DRIVER_DESC,
+ .connect = sw_connect,
+ .disconnect = sw_disconnect,
+};
+
+static int __init sw_init(void)
+{
+ gameport_register_driver(&sw_drv);
+ return 0;
+}
+
+static void __exit sw_exit(void)
+{
+ gameport_unregister_driver(&sw_drv);
+}
+
+module_init(sw_init);
+module_exit(sw_exit);
diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c
new file mode 100644
index 000000000000..ec0a2a64d49c
--- /dev/null
+++ b/drivers/input/joystick/spaceball.c
@@ -0,0 +1,319 @@
+/*
+ * $Id: spaceball.c,v 1.17 2002/01/22 20:29:03 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * David Thompson
+ * Joseph Krahn
+ */
+
+/*
+ * SpaceTec SpaceBall 2003/3003/4000 FLX 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/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "SpaceTec SpaceBall 2003/3003/4000 FLX driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define SPACEBALL_MAX_LENGTH 128
+#define SPACEBALL_MAX_ID 8
+
+#define SPACEBALL_1003 1
+#define SPACEBALL_2003B 3
+#define SPACEBALL_2003C 4
+#define SPACEBALL_3003C 7
+#define SPACEBALL_4000FLX 8
+#define SPACEBALL_4000FLX_L 9
+
+static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY };
+static char *spaceball_names[] = {
+ "?", "SpaceTec SpaceBall 1003", "SpaceTec SpaceBall 2003", "SpaceTec SpaceBall 2003B",
+ "SpaceTec SpaceBall 2003C", "SpaceTec SpaceBall 3003", "SpaceTec SpaceBall SpaceController",
+ "SpaceTec SpaceBall 3003C", "SpaceTec SpaceBall 4000FLX", "SpaceTec SpaceBall 4000FLX Lefty" };
+
+/*
+ * Per-Ball data.
+ */
+
+struct spaceball {
+ struct input_dev dev;
+ struct serio *serio;
+ int idx;
+ int escape;
+ unsigned char data[SPACEBALL_MAX_LENGTH];
+ char phys[32];
+};
+
+/*
+ * spaceball_process_packet() decodes packets the driver receives from the
+ * SpaceBall.
+ */
+
+static void spaceball_process_packet(struct spaceball* spaceball, struct pt_regs *regs)
+{
+ struct input_dev *dev = &spaceball->dev;
+ unsigned char *data = spaceball->data;
+ int i;
+
+ if (spaceball->idx < 2) return;
+
+ input_regs(dev, regs);
+
+ switch (spaceball->data[0]) {
+
+ case 'D': /* Ball data */
+ if (spaceball->idx != 15) return;
+ for (i = 0; i < 6; i++)
+ input_report_abs(dev, spaceball_axes[i],
+ (__s16)((data[2 * i + 3] << 8) | data[2 * i + 2]));
+ break;
+
+ case 'K': /* Button data */
+ if (spaceball->idx != 3) return;
+ input_report_key(dev, BTN_1, (data[2] & 0x01) || (data[2] & 0x20));
+ input_report_key(dev, BTN_2, data[2] & 0x02);
+ input_report_key(dev, BTN_3, data[2] & 0x04);
+ input_report_key(dev, BTN_4, data[2] & 0x08);
+ input_report_key(dev, BTN_5, data[1] & 0x01);
+ input_report_key(dev, BTN_6, data[1] & 0x02);
+ input_report_key(dev, BTN_7, data[1] & 0x04);
+ input_report_key(dev, BTN_8, data[1] & 0x10);
+ break;
+
+ case '.': /* Advanced button data */
+ if (spaceball->idx != 3) return;
+ input_report_key(dev, BTN_1, data[2] & 0x01);
+ input_report_key(dev, BTN_2, data[2] & 0x02);
+ input_report_key(dev, BTN_3, data[2] & 0x04);
+ input_report_key(dev, BTN_4, data[2] & 0x08);
+ input_report_key(dev, BTN_5, data[2] & 0x10);
+ input_report_key(dev, BTN_6, data[2] & 0x20);
+ input_report_key(dev, BTN_7, data[2] & 0x80);
+ input_report_key(dev, BTN_8, data[1] & 0x01);
+ input_report_key(dev, BTN_9, data[1] & 0x02);
+ input_report_key(dev, BTN_A, data[1] & 0x04);
+ input_report_key(dev, BTN_B, data[1] & 0x08);
+ input_report_key(dev, BTN_C, data[1] & 0x10);
+ input_report_key(dev, BTN_MODE, data[1] & 0x20);
+ break;
+
+ case 'E': /* Device error */
+ spaceball->data[spaceball->idx - 1] = 0;
+ printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1);
+ break;
+
+ case '?': /* Bad command packet */
+ spaceball->data[spaceball->idx - 1] = 0;
+ printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1);
+ break;
+ }
+
+ input_sync(dev);
+}
+
+/*
+ * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor,
+ * and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which
+ * can occur in the axis values.
+ */
+
+static irqreturn_t spaceball_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct spaceball *spaceball = serio_get_drvdata(serio);
+
+ switch (data) {
+ case 0xd:
+ spaceball_process_packet(spaceball, regs);
+ spaceball->idx = 0;
+ spaceball->escape = 0;
+ break;
+ case '^':
+ if (!spaceball->escape) {
+ spaceball->escape = 1;
+ break;
+ }
+ spaceball->escape = 0;
+ case 'M':
+ case 'Q':
+ case 'S':
+ if (spaceball->escape) {
+ spaceball->escape = 0;
+ data &= 0x1f;
+ }
+ default:
+ if (spaceball->escape)
+ spaceball->escape = 0;
+ if (spaceball->idx < SPACEBALL_MAX_LENGTH)
+ spaceball->data[spaceball->idx++] = data;
+ break;
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * spaceball_disconnect() is the opposite of spaceball_connect()
+ */
+
+static void spaceball_disconnect(struct serio *serio)
+{
+ struct spaceball* spaceball = serio_get_drvdata(serio);
+
+ input_unregister_device(&spaceball->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(spaceball);
+}
+
+/*
+ * spaceball_connect() is the routine that is called when someone adds a
+ * new serio device that supports Spaceball protocol and registers it as
+ * an input device.
+ */
+
+static int spaceball_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct spaceball *spaceball;
+ int i, t, id;
+ int err;
+
+ if ((id = serio->id.id) > SPACEBALL_MAX_ID)
+ return -ENODEV;
+
+ if (!(spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL)))
+ return - ENOMEM;
+
+ memset(spaceball, 0, sizeof(struct spaceball));
+
+ spaceball->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ switch (id) {
+ case SPACEBALL_4000FLX:
+ case SPACEBALL_4000FLX_L:
+ spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_9);
+ spaceball->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_MODE);
+ default:
+ spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4)
+ | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7) | BIT(BTN_8);
+ case SPACEBALL_3003C:
+ spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_1) | BIT(BTN_8);
+ }
+
+ for (i = 0; i < 6; i++) {
+ t = spaceball_axes[i];
+ set_bit(t, spaceball->dev.absbit);
+ spaceball->dev.absmin[t] = i < 3 ? -8000 : -1600;
+ spaceball->dev.absmax[t] = i < 3 ? 8000 : 1600;
+ spaceball->dev.absflat[t] = i < 3 ? 40 : 8;
+ spaceball->dev.absfuzz[t] = i < 3 ? 8 : 2;
+ }
+
+ spaceball->serio = serio;
+ spaceball->dev.private = spaceball;
+
+ sprintf(spaceball->phys, "%s/input0", serio->phys);
+
+ init_input_dev(&spaceball->dev);
+ spaceball->dev.name = spaceball_names[id];
+ spaceball->dev.phys = spaceball->phys;
+ spaceball->dev.id.bustype = BUS_RS232;
+ spaceball->dev.id.vendor = SERIO_SPACEBALL;
+ spaceball->dev.id.product = id;
+ spaceball->dev.id.version = 0x0100;
+ spaceball->dev.dev = &serio->dev;
+
+ serio_set_drvdata(serio, spaceball);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(spaceball);
+ return err;
+ }
+
+ input_register_device(&spaceball->dev);
+
+ printk(KERN_INFO "input: %s on serio%s\n",
+ spaceball_names[id], serio->phys);
+
+ return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id spaceball_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_SPACEBALL,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, spaceball_serio_ids);
+
+static struct serio_driver spaceball_drv = {
+ .driver = {
+ .name = "spaceball",
+ },
+ .description = DRIVER_DESC,
+ .id_table = spaceball_serio_ids,
+ .interrupt = spaceball_interrupt,
+ .connect = spaceball_connect,
+ .disconnect = spaceball_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init spaceball_init(void)
+{
+ serio_register_driver(&spaceball_drv);
+ return 0;
+}
+
+static void __exit spaceball_exit(void)
+{
+ serio_unregister_driver(&spaceball_drv);
+}
+
+module_init(spaceball_init);
+module_exit(spaceball_exit);
diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c
new file mode 100644
index 000000000000..c76cf8ff29c0
--- /dev/null
+++ b/drivers/input/joystick/spaceorb.c
@@ -0,0 +1,263 @@
+/*
+ * $Id: spaceorb.c,v 1.15 2002/01/22 20:29:19 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * David Thompson
+ */
+
+/*
+ * SpaceTec SpaceOrb 360 and Avenger 6dof 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 <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "SpaceTec SpaceOrb 360 and Avenger 6dof controller driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define SPACEORB_MAX_LENGTH 64
+
+static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A };
+static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+static char *spaceorb_name = "SpaceTec SpaceOrb 360 / Avenger";
+
+/*
+ * Per-Orb data.
+ */
+
+struct spaceorb {
+ struct input_dev dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[SPACEORB_MAX_LENGTH];
+ char phys[32];
+};
+
+static unsigned char spaceorb_xor[] = "SpaceWare";
+
+static unsigned char *spaceorb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout",
+ "Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" };
+
+/*
+ * spaceorb_process_packet() decodes packets the driver receives from the
+ * SpaceOrb.
+ */
+
+static void spaceorb_process_packet(struct spaceorb *spaceorb, struct pt_regs *regs)
+{
+ struct input_dev *dev = &spaceorb->dev;
+ unsigned char *data = spaceorb->data;
+ unsigned char c = 0;
+ int axes[6];
+ int i;
+
+ if (spaceorb->idx < 2) return;
+ for (i = 0; i < spaceorb->idx; i++) c ^= data[i];
+ if (c) return;
+
+ input_regs(dev, regs);
+
+ switch (data[0]) {
+
+ case 'R': /* Reset packet */
+ spaceorb->data[spaceorb->idx - 1] = 0;
+ for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++);
+ printk(KERN_INFO "input: %s [%s] on %s\n",
+ spaceorb_name, spaceorb->data + i, spaceorb->serio->phys);
+ break;
+
+ case 'D': /* Ball + button data */
+ if (spaceorb->idx != 12) return;
+ for (i = 0; i < 9; i++) spaceorb->data[i+2] ^= spaceorb_xor[i];
+ axes[0] = ( data[2] << 3) | (data[ 3] >> 4);
+ axes[1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1);
+ axes[2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5);
+ axes[3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2);
+ axes[4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6);
+ axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3);
+ for (i = 0; i < 6; i++)
+ input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0));
+ for (i = 0; i < 6; i++)
+ input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1);
+ break;
+
+ case 'K': /* Button data */
+ if (spaceorb->idx != 5) return;
+ for (i = 0; i < 7; i++)
+ input_report_key(dev, spaceorb_buttons[i], (data[2] >> i) & 1);
+
+ break;
+
+ case 'E': /* Error packet */
+ if (spaceorb->idx != 4) return;
+ printk(KERN_ERR "joy-spaceorb: Device error. [ ");
+ for (i = 0; i < 7; i++) if (data[1] & (1 << i)) printk("%s ", spaceorb_errors[i]);
+ printk("]\n");
+ break;
+ }
+
+ input_sync(dev);
+}
+
+static irqreturn_t spaceorb_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct spaceorb* spaceorb = serio_get_drvdata(serio);
+
+ if (~data & 0x80) {
+ if (spaceorb->idx) spaceorb_process_packet(spaceorb, regs);
+ spaceorb->idx = 0;
+ }
+ if (spaceorb->idx < SPACEORB_MAX_LENGTH)
+ spaceorb->data[spaceorb->idx++] = data & 0x7f;
+ return IRQ_HANDLED;
+}
+
+/*
+ * spaceorb_disconnect() is the opposite of spaceorb_connect()
+ */
+
+static void spaceorb_disconnect(struct serio *serio)
+{
+ struct spaceorb* spaceorb = serio_get_drvdata(serio);
+
+ input_unregister_device(&spaceorb->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(spaceorb);
+}
+
+/*
+ * spaceorb_connect() is the routine that is called when someone adds a
+ * new serio device that supports SpaceOrb/Avenger protocol and registers
+ * it as an input device.
+ */
+
+static int spaceorb_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct spaceorb *spaceorb;
+ int i, t;
+ int err;
+
+ if (!(spaceorb = kmalloc(sizeof(struct spaceorb), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(spaceorb, 0, sizeof(struct spaceorb));
+
+ spaceorb->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (i = 0; i < 6; i++)
+ set_bit(spaceorb_buttons[i], spaceorb->dev.keybit);
+
+ for (i = 0; i < 6; i++) {
+ t = spaceorb_axes[i];
+ set_bit(t, spaceorb->dev.absbit);
+ spaceorb->dev.absmin[t] = -508;
+ spaceorb->dev.absmax[t] = 508;
+ }
+
+ spaceorb->serio = serio;
+ spaceorb->dev.private = spaceorb;
+
+ sprintf(spaceorb->phys, "%s/input0", serio->phys);
+
+ init_input_dev(&spaceorb->dev);
+ spaceorb->dev.name = spaceorb_name;
+ spaceorb->dev.phys = spaceorb->phys;
+ spaceorb->dev.id.bustype = BUS_RS232;
+ spaceorb->dev.id.vendor = SERIO_SPACEORB;
+ spaceorb->dev.id.product = 0x0001;
+ spaceorb->dev.id.version = 0x0100;
+ spaceorb->dev.dev = &serio->dev;
+
+ serio_set_drvdata(serio, spaceorb);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(spaceorb);
+ return err;
+ }
+
+ input_register_device(&spaceorb->dev);
+
+ return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id spaceorb_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_SPACEORB,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, spaceorb_serio_ids);
+
+static struct serio_driver spaceorb_drv = {
+ .driver = {
+ .name = "spaceorb",
+ },
+ .description = DRIVER_DESC,
+ .id_table = spaceorb_serio_ids,
+ .interrupt = spaceorb_interrupt,
+ .connect = spaceorb_connect,
+ .disconnect = spaceorb_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init spaceorb_init(void)
+{
+ serio_register_driver(&spaceorb_drv);
+ return 0;
+}
+
+static void __exit spaceorb_exit(void)
+{
+ serio_unregister_driver(&spaceorb_drv);
+}
+
+module_init(spaceorb_init);
+module_exit(spaceorb_exit);
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
new file mode 100644
index 000000000000..6f6e6753d590
--- /dev/null
+++ b/drivers/input/joystick/stinger.c
@@ -0,0 +1,236 @@
+/*
+ * $Id: stinger.c,v 1.10 2002/01/22 20:29:31 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ * Copyright (c) 2000 Mark Fletcher
+ */
+
+/*
+ * Gravis Stinger gamepad driver for Linux
+ */
+
+/*
+ * This program is free warftware; 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/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Gravis Stinger gamepad driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define STINGER_MAX_LENGTH 8
+
+static char *stinger_name = "Gravis Stinger";
+
+/*
+ * Per-Stinger data.
+ */
+
+struct stinger {
+ struct input_dev dev;
+ int idx;
+ unsigned char data[STINGER_MAX_LENGTH];
+ char phys[32];
+};
+
+/*
+ * stinger_process_packet() decodes packets the driver receives from the
+ * Stinger. It updates the data accordingly.
+ */
+
+static void stinger_process_packet(struct stinger *stinger, struct pt_regs *regs)
+{
+ struct input_dev *dev = &stinger->dev;
+ unsigned char *data = stinger->data;
+
+ if (!stinger->idx) return;
+
+ input_regs(dev, regs);
+
+ input_report_key(dev, BTN_A, ((data[0] & 0x20) >> 5));
+ input_report_key(dev, BTN_B, ((data[0] & 0x10) >> 4));
+ input_report_key(dev, BTN_C, ((data[0] & 0x08) >> 3));
+ input_report_key(dev, BTN_X, ((data[0] & 0x04) >> 2));
+ input_report_key(dev, BTN_Y, ((data[3] & 0x20) >> 5));
+ input_report_key(dev, BTN_Z, ((data[3] & 0x10) >> 4));
+ input_report_key(dev, BTN_TL, ((data[3] & 0x08) >> 3));
+ input_report_key(dev, BTN_TR, ((data[3] & 0x04) >> 2));
+ input_report_key(dev, BTN_SELECT, ((data[3] & 0x02) >> 1));
+ input_report_key(dev, BTN_START, (data[3] & 0x01));
+
+ input_report_abs(dev, ABS_X, (data[1] & 0x3F) - ((data[0] & 0x01) << 6));
+ input_report_abs(dev, ABS_Y, ((data[0] & 0x02) << 5) - (data[2] & 0x3F));
+
+ input_sync(dev);
+
+ return;
+}
+
+/*
+ * stinger_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 irqreturn_t stinger_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct stinger *stinger = serio_get_drvdata(serio);
+
+ /* All Stinger packets are 4 bytes */
+
+ if (stinger->idx < STINGER_MAX_LENGTH)
+ stinger->data[stinger->idx++] = data;
+
+ if (stinger->idx == 4) {
+ stinger_process_packet(stinger, regs);
+ stinger->idx = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * stinger_disconnect() is the opposite of stinger_connect()
+ */
+
+static void stinger_disconnect(struct serio *serio)
+{
+ struct stinger *stinger = serio_get_drvdata(serio);
+
+ input_unregister_device(&stinger->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(stinger);
+}
+
+/*
+ * stinger_connect() is the routine that is called when someone adds a
+ * new serio device that supports Stinger protocol and registers it as
+ * an input device.
+ */
+
+static int stinger_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct stinger *stinger;
+ int i;
+ int err;
+
+ if (!(stinger = kmalloc(sizeof(struct stinger), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(stinger, 0, sizeof(struct stinger));
+
+ stinger->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ stinger->dev.keybit[LONG(BTN_A)] = BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_X) | \
+ BIT(BTN_Y) | BIT(BTN_Z) | BIT(BTN_TL) | BIT(BTN_TR) | \
+ BIT(BTN_START) | BIT(BTN_SELECT);
+ stinger->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+
+ sprintf(stinger->phys, "%s/serio0", serio->phys);
+
+ init_input_dev(&stinger->dev);
+ stinger->dev.name = stinger_name;
+ stinger->dev.phys = stinger->phys;
+ stinger->dev.id.bustype = BUS_RS232;
+ stinger->dev.id.vendor = SERIO_STINGER;
+ stinger->dev.id.product = 0x0001;
+ stinger->dev.id.version = 0x0100;
+ stinger->dev.dev = &serio->dev;
+
+ for (i = 0; i < 2; i++) {
+ stinger->dev.absmax[ABS_X+i] = 64;
+ stinger->dev.absmin[ABS_X+i] = -64;
+ stinger->dev.absflat[ABS_X+i] = 4;
+ }
+
+ stinger->dev.private = stinger;
+
+ serio_set_drvdata(serio, stinger);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(stinger);
+ return err;
+ }
+
+ input_register_device(&stinger->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", stinger_name, serio->phys);
+
+ return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id stinger_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_STINGER,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, stinger_serio_ids);
+
+static struct serio_driver stinger_drv = {
+ .driver = {
+ .name = "stinger",
+ },
+ .description = DRIVER_DESC,
+ .id_table = stinger_serio_ids,
+ .interrupt = stinger_interrupt,
+ .connect = stinger_connect,
+ .disconnect = stinger_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init stinger_init(void)
+{
+ serio_register_driver(&stinger_drv);
+ return 0;
+}
+
+static void __exit stinger_exit(void)
+{
+ serio_unregister_driver(&stinger_drv);
+}
+
+module_init(stinger_init);
+module_exit(stinger_exit);
diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c
new file mode 100644
index 000000000000..aaee52ceb920
--- /dev/null
+++ b/drivers/input/joystick/tmdc.c
@@ -0,0 +1,384 @@
+/*
+ * $Id: tmdc.c,v 1.31 2002/01/22 20:29:52 vojtech Exp $
+ *
+ * Copyright (c) 1998-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Trystan Larey-Williams
+ */
+
+/*
+ * ThrustMaster DirectConnect (BSP) joystick family 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC "ThrustMaster DirectConnect joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define TMDC_MAX_START 600 /* 600 us */
+#define TMDC_MAX_STROBE 60 /* 60 us */
+#define TMDC_MAX_LENGTH 13
+
+#define TMDC_MODE_M3DI 1
+#define TMDC_MODE_3DRP 3
+#define TMDC_MODE_AT 4
+#define TMDC_MODE_FM 8
+#define TMDC_MODE_FGP 163
+
+#define TMDC_BYTE_ID 10
+#define TMDC_BYTE_REV 11
+#define TMDC_BYTE_DEF 12
+
+#define TMDC_ABS 7
+#define TMDC_ABS_HAT 4
+#define TMDC_BTN 16
+
+static unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 };
+static unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 };
+
+static signed char tmdc_abs[TMDC_ABS] =
+ { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ };
+static signed char tmdc_abs_hat[TMDC_ABS_HAT] =
+ { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+static signed char tmdc_abs_at[TMDC_ABS] =
+ { ABS_X, ABS_Y, ABS_RUDDER, -1, ABS_THROTTLE };
+static signed char tmdc_abs_fm[TMDC_ABS] =
+ { ABS_RX, ABS_RY, ABS_X, ABS_Y };
+
+static short tmdc_btn_pad[TMDC_BTN] =
+ { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR };
+static short tmdc_btn_joy[TMDC_BTN] =
+ { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE,
+ BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z };
+static short tmdc_btn_fm[TMDC_BTN] =
+ { BTN_TRIGGER, BTN_C, BTN_B, BTN_A, BTN_THUMB, BTN_X, BTN_Y, BTN_Z, BTN_TOP, BTN_TOP2 };
+static short tmdc_btn_at[TMDC_BTN] =
+ { BTN_TRIGGER, BTN_THUMB2, BTN_PINKIE, BTN_THUMB, BTN_BASE6, BTN_BASE5, BTN_BASE4,
+ BTN_BASE3, BTN_BASE2, BTN_BASE };
+
+static struct {
+ int x;
+ int y;
+} tmdc_hat_to_axis[] = {{ 0, 0}, { 1, 0}, { 0,-1}, {-1, 0}, { 0, 1}};
+
+struct tmdc {
+ struct gameport *gameport;
+ struct input_dev dev[2];
+ char name[2][64];
+ char phys[2][32];
+ int mode[2];
+ signed char *abs[2];
+ short *btn[2];
+ unsigned char absc[2];
+ unsigned char btnc[2][4];
+ unsigned char btno[2][4];
+ int reads;
+ int bads;
+ unsigned char exists;
+};
+
+/*
+ * tmdc_read_packet() reads a ThrustMaster packet.
+ */
+
+static int tmdc_read_packet(struct gameport *gameport, unsigned char data[2][TMDC_MAX_LENGTH])
+{
+ unsigned char u, v, w, x;
+ unsigned long flags;
+ int i[2], j[2], t[2], p, k;
+
+ p = gameport_time(gameport, TMDC_MAX_STROBE);
+
+ for (k = 0; k < 2; k++) {
+ t[k] = gameport_time(gameport, TMDC_MAX_START);
+ i[k] = j[k] = 0;
+ }
+
+ local_irq_save(flags);
+ gameport_trigger(gameport);
+
+ w = gameport_read(gameport) >> 4;
+
+ do {
+ x = w;
+ w = gameport_read(gameport) >> 4;
+
+ for (k = 0, v = w, u = x; k < 2; k++, v >>= 2, u >>= 2) {
+ if (~v & u & 2) {
+ if (t[k] <= 0 || i[k] >= TMDC_MAX_LENGTH) continue;
+ t[k] = p;
+ if (j[k] == 0) { /* Start bit */
+ if (~v & 1) t[k] = 0;
+ data[k][i[k]] = 0; j[k]++; continue;
+ }
+ if (j[k] == 9) { /* Stop bit */
+ if (v & 1) t[k] = 0;
+ j[k] = 0; i[k]++; continue;
+ }
+ data[k][i[k]] |= (~v & 1) << (j[k]++ - 1); /* Data bit */
+ }
+ t[k]--;
+ }
+ } while (t[0] > 0 || t[1] > 0);
+
+ local_irq_restore(flags);
+
+ return (i[0] == TMDC_MAX_LENGTH) | ((i[1] == TMDC_MAX_LENGTH) << 1);
+}
+
+/*
+ * tmdc_poll() reads and analyzes ThrustMaster joystick data.
+ */
+
+static void tmdc_poll(struct gameport *gameport)
+{
+ unsigned char data[2][TMDC_MAX_LENGTH];
+ struct tmdc *tmdc = gameport_get_drvdata(gameport);
+ struct input_dev *dev;
+ unsigned char r, bad = 0;
+ int i, j, k, l;
+
+ tmdc->reads++;
+
+ if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists)
+ bad = 1;
+ else
+
+ for (j = 0; j < 2; j++)
+ if (r & (1 << j) & tmdc->exists) {
+
+ if (data[j][TMDC_BYTE_ID] != tmdc->mode[j]) {
+ bad = 1;
+ continue;
+ }
+
+ dev = tmdc->dev + j;
+
+ for (i = 0; i < tmdc->absc[j]; i++) {
+ if (tmdc->abs[j][i] < 0) continue;
+ input_report_abs(dev, tmdc->abs[j][i], data[j][tmdc_byte_a[i]]);
+ }
+
+ switch (tmdc->mode[j]) {
+
+ case TMDC_MODE_M3DI:
+
+ i = tmdc_byte_d[0];
+ input_report_abs(dev, ABS_HAT0X, ((data[j][i] >> 3) & 1) - ((data[j][i] >> 1) & 1));
+ input_report_abs(dev, ABS_HAT0Y, ((data[j][i] >> 2) & 1) - ( data[j][i] & 1));
+ break;
+
+ case TMDC_MODE_AT:
+
+ i = tmdc_byte_a[3];
+ input_report_abs(dev, ABS_HAT0X, tmdc_hat_to_axis[(data[j][i] - 141) / 25].x);
+ input_report_abs(dev, ABS_HAT0Y, tmdc_hat_to_axis[(data[j][i] - 141) / 25].y);
+ break;
+
+ }
+
+ for (k = l = 0; k < 4; k++) {
+ for (i = 0; i < tmdc->btnc[j][k]; i++)
+ input_report_key(dev, tmdc->btn[j][i + l],
+ ((data[j][tmdc_byte_d[k]] >> (i + tmdc->btno[j][k])) & 1));
+ l += tmdc->btnc[j][k];
+ }
+
+ input_sync(dev);
+ }
+
+ tmdc->bads += bad;
+}
+
+static int tmdc_open(struct input_dev *dev)
+{
+ struct tmdc *tmdc = dev->private;
+
+ gameport_start_polling(tmdc->gameport);
+ return 0;
+}
+
+static void tmdc_close(struct input_dev *dev)
+{
+ struct tmdc *tmdc = dev->private;
+
+ gameport_stop_polling(tmdc->gameport);
+}
+
+/*
+ * tmdc_probe() probes for ThrustMaster type joysticks.
+ */
+
+static int tmdc_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ static struct models {
+ unsigned char id;
+ char *name;
+ char abs;
+ char hats;
+ char btnc[4];
+ char btno[4];
+ signed char *axes;
+ short *buttons;
+ } models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy },
+ { 3, "ThrustMaster Rage 3D Gamepad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
+ { 4, "ThrustMaster Attack Throttle", 5, 2, { 4, 6 }, { 4, 2 }, tmdc_abs_at, tmdc_btn_at },
+ { 8, "ThrustMaster FragMaster", 4, 0, { 8, 2 }, { 0, 0 }, tmdc_abs_fm, tmdc_btn_fm },
+ { 163, "Thrustmaster Fusion GamePad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
+ { 0, "Unknown %d-axis, %d-button TM device %d", 0, 0, { 0, 0 }, { 0, 0 }, tmdc_abs, tmdc_btn_joy }};
+
+ unsigned char data[2][TMDC_MAX_LENGTH];
+ struct tmdc *tmdc;
+ int i, j, k, l, m;
+ int err;
+
+ if (!(tmdc = kcalloc(1, sizeof(struct tmdc), GFP_KERNEL)))
+ return -ENOMEM;
+
+ tmdc->gameport = gameport;
+
+ gameport_set_drvdata(gameport, tmdc);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ if (!(tmdc->exists = tmdc_read_packet(gameport, data))) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ gameport_set_poll_handler(gameport, tmdc_poll);
+ gameport_set_poll_interval(gameport, 20);
+
+ for (j = 0; j < 2; j++)
+ if (tmdc->exists & (1 << j)) {
+
+ tmdc->mode[j] = data[j][TMDC_BYTE_ID];
+
+ for (m = 0; models[m].id && models[m].id != tmdc->mode[j]; m++);
+
+ tmdc->abs[j] = models[m].axes;
+ tmdc->btn[j] = models[m].buttons;
+
+ if (!models[m].id) {
+ models[m].abs = data[j][TMDC_BYTE_DEF] >> 4;
+ for (k = 0; k < 4; k++)
+ models[m].btnc[k] = k < (data[j][TMDC_BYTE_DEF] & 0xf) ? 8 : 0;
+ }
+
+ tmdc->absc[j] = models[m].abs;
+ for (k = 0; k < 4; k++) {
+ tmdc->btnc[j][k] = models[m].btnc[k];
+ tmdc->btno[j][k] = models[m].btno[k];
+ }
+
+ sprintf(tmdc->name[j], models[m].name, models[m].abs,
+ (data[j][TMDC_BYTE_DEF] & 0xf) << 3, tmdc->mode[j]);
+
+ sprintf(tmdc->phys[j], "%s/input%d", gameport->phys, j);
+
+ tmdc->dev[j].private = tmdc;
+ tmdc->dev[j].open = tmdc_open;
+ tmdc->dev[j].close = tmdc_close;
+
+ tmdc->dev[j].name = tmdc->name[j];
+ tmdc->dev[j].phys = tmdc->phys[j];
+ tmdc->dev[j].id.bustype = BUS_GAMEPORT;
+ tmdc->dev[j].id.vendor = GAMEPORT_ID_VENDOR_THRUSTMASTER;
+ tmdc->dev[j].id.product = models[m].id;
+ tmdc->dev[j].id.version = 0x0100;
+
+ tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ for (i = 0; i < models[m].abs && i < TMDC_ABS; i++)
+ if (tmdc->abs[j][i] >= 0)
+ input_set_abs_params(&tmdc->dev[j], tmdc->abs[j][i], 8, 248, 2, 4);
+
+ for (i = 0; i < models[m].hats && i < TMDC_ABS_HAT; i++)
+ input_set_abs_params(&tmdc->dev[j], tmdc_abs_hat[i], -1, 1, 0, 0);
+
+
+ for (k = l = 0; k < 4; k++) {
+ for (i = 0; i < models[m].btnc[k] && i < TMDC_BTN; i++)
+ set_bit(tmdc->btn[j][i + l], tmdc->dev[j].keybit);
+ l += models[m].btnc[k];
+ }
+
+ input_register_device(tmdc->dev + j);
+ printk(KERN_INFO "input: %s on %s\n", tmdc->name[j], gameport->phys);
+ }
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(tmdc);
+ return err;
+}
+
+static void tmdc_disconnect(struct gameport *gameport)
+{
+ struct tmdc *tmdc = gameport_get_drvdata(gameport);
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (tmdc->exists & (1 << i))
+ input_unregister_device(tmdc->dev + i);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(tmdc);
+}
+
+static struct gameport_driver tmdc_drv = {
+ .driver = {
+ .name = "tmdc",
+ },
+ .description = DRIVER_DESC,
+ .connect = tmdc_connect,
+ .disconnect = tmdc_disconnect,
+};
+
+static int __init tmdc_init(void)
+{
+ gameport_register_driver(&tmdc_drv);
+ return 0;
+}
+
+static void __exit tmdc_exit(void)
+{
+ gameport_unregister_driver(&tmdc_drv);
+}
+
+module_init(tmdc_init);
+module_exit(tmdc_exit);
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
new file mode 100644
index 000000000000..dd88b9cb49fa
--- /dev/null
+++ b/drivers/input/joystick/turbografx.c
@@ -0,0 +1,258 @@
+/*
+ * $Id: turbografx.c,v 1.14 2002/01/22 20:30:39 vojtech Exp $
+ *
+ * Copyright (c) 1998-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Steffen Schwenke
+ */
+
+/*
+ * TurboGraFX parallel port interface 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/parport.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("TurboGraFX parallel port interface driver");
+MODULE_LICENSE("GPL");
+
+static int tgfx[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+static int tgfx_nargs __initdata = 0;
+module_param_array_named(map, tgfx, int, &tgfx_nargs, 0);
+MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>");
+
+static int tgfx_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+static int tgfx_nargs_2 __initdata = 0;
+module_param_array_named(map2, tgfx_2, int, &tgfx_nargs_2, 0);
+MODULE_PARM_DESC(map2, "Describes second set of devices");
+
+static int tgfx_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+static int tgfx_nargs_3 __initdata = 0;
+module_param_array_named(map3, tgfx_3, int, &tgfx_nargs_3, 0);
+MODULE_PARM_DESC(map3, "Describes third set of devices");
+
+__obsolete_setup("tgfx=");
+__obsolete_setup("tgfx_2=");
+__obsolete_setup("tgfx_3=");
+
+#define TGFX_REFRESH_TIME HZ/100 /* 10 ms */
+
+#define TGFX_TRIGGER 0x08
+#define TGFX_UP 0x10
+#define TGFX_DOWN 0x20
+#define TGFX_LEFT 0x40
+#define TGFX_RIGHT 0x80
+
+#define TGFX_THUMB 0x02
+#define TGFX_THUMB2 0x04
+#define TGFX_TOP 0x01
+#define TGFX_TOP2 0x08
+
+static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 };
+static char *tgfx_name = "TurboGraFX Multisystem joystick";
+
+static struct tgfx {
+ struct pardevice *pd;
+ struct timer_list timer;
+ struct input_dev dev[7];
+ char phys[7][32];
+ int sticks;
+ int used;
+} *tgfx_base[3];
+
+/*
+ * tgfx_timer() reads and analyzes TurboGraFX joystick data.
+ */
+
+static void tgfx_timer(unsigned long private)
+{
+ struct tgfx *tgfx = (void *) private;
+ struct input_dev *dev;
+ int data1, data2, i;
+
+ for (i = 0; i < 7; i++)
+ if (tgfx->sticks & (1 << i)) {
+
+ dev = tgfx->dev + i;
+
+ parport_write_data(tgfx->pd->port, ~(1 << i));
+ data1 = parport_read_status(tgfx->pd->port) ^ 0x7f;
+ data2 = parport_read_control(tgfx->pd->port) ^ 0x04; /* CAVEAT parport */
+
+ input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT));
+ input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP ));
+
+ input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER));
+ input_report_key(dev, BTN_THUMB, (data2 & TGFX_THUMB ));
+ input_report_key(dev, BTN_THUMB2, (data2 & TGFX_THUMB2 ));
+ input_report_key(dev, BTN_TOP, (data2 & TGFX_TOP ));
+ input_report_key(dev, BTN_TOP2, (data2 & TGFX_TOP2 ));
+
+ input_sync(dev);
+ }
+
+ mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
+}
+
+static int tgfx_open(struct input_dev *dev)
+{
+ struct tgfx *tgfx = dev->private;
+ if (!tgfx->used++) {
+ parport_claim(tgfx->pd);
+ parport_write_control(tgfx->pd->port, 0x04);
+ mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
+ }
+ return 0;
+}
+
+static void tgfx_close(struct input_dev *dev)
+{
+ struct tgfx *tgfx = dev->private;
+ if (!--tgfx->used) {
+ del_timer(&tgfx->timer);
+ parport_write_control(tgfx->pd->port, 0x00);
+ parport_release(tgfx->pd);
+ }
+}
+
+/*
+ * tgfx_probe() probes for tg gamepads.
+ */
+
+static struct tgfx __init *tgfx_probe(int *config, int nargs)
+{
+ struct tgfx *tgfx;
+ struct parport *pp;
+ int i, j;
+
+ if (config[0] < 0)
+ return NULL;
+
+ if (nargs < 2) {
+ printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n");
+ return NULL;
+ }
+
+ pp = parport_find_number(config[0]);
+
+ if (!pp) {
+ printk(KERN_ERR "turbografx.c: no such parport\n");
+ return NULL;
+ }
+
+ if (!(tgfx = kmalloc(sizeof(struct tgfx), GFP_KERNEL))) {
+ parport_put_port(pp);
+ return NULL;
+ }
+ memset(tgfx, 0, sizeof(struct tgfx));
+
+ tgfx->pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+
+ parport_put_port(pp);
+
+ if (!tgfx->pd) {
+ printk(KERN_ERR "turbografx.c: parport busy already - lp.o loaded?\n");
+ kfree(tgfx);
+ return NULL;
+ }
+
+ init_timer(&tgfx->timer);
+ tgfx->timer.data = (long) tgfx;
+ tgfx->timer.function = tgfx_timer;
+
+ tgfx->sticks = 0;
+
+ for (i = 0; i < nargs - 1; i++)
+ if (config[i+1] > 0 && config[i+1] < 6) {
+
+ tgfx->sticks |= (1 << i);
+
+ tgfx->dev[i].private = tgfx;
+ tgfx->dev[i].open = tgfx_open;
+ tgfx->dev[i].close = tgfx_close;
+
+ sprintf(tgfx->phys[i], "%s/input0", tgfx->pd->port->name);
+
+ tgfx->dev[i].name = tgfx_name;
+ tgfx->dev[i].phys = tgfx->phys[i];
+ tgfx->dev[i].id.bustype = BUS_PARPORT;
+ tgfx->dev[i].id.vendor = 0x0003;
+ tgfx->dev[i].id.product = config[i+1];
+ tgfx->dev[i].id.version = 0x0100;
+
+ tgfx->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ tgfx->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+
+ for (j = 0; j < config[i+1]; j++)
+ set_bit(tgfx_buttons[j], tgfx->dev[i].keybit);
+
+ tgfx->dev[i].absmin[ABS_X] = -1; tgfx->dev[i].absmax[ABS_X] = 1;
+ tgfx->dev[i].absmin[ABS_Y] = -1; tgfx->dev[i].absmax[ABS_Y] = 1;
+
+ input_register_device(tgfx->dev + i);
+ printk(KERN_INFO "input: %d-button Multisystem joystick on %s\n",
+ config[i+1], tgfx->pd->port->name);
+ }
+
+ if (!tgfx->sticks) {
+ parport_unregister_device(tgfx->pd);
+ kfree(tgfx);
+ return NULL;
+ }
+
+ return tgfx;
+}
+
+static int __init tgfx_init(void)
+{
+ tgfx_base[0] = tgfx_probe(tgfx, tgfx_nargs);
+ tgfx_base[1] = tgfx_probe(tgfx_2, tgfx_nargs_2);
+ tgfx_base[2] = tgfx_probe(tgfx_3, tgfx_nargs_3);
+
+ if (tgfx_base[0] || tgfx_base[1] || tgfx_base[2])
+ return 0;
+
+ return -ENODEV;
+}
+
+static void __exit tgfx_exit(void)
+{
+ int i, j;
+
+ for (i = 0; i < 3; i++)
+ if (tgfx_base[i]) {
+ for (j = 0; j < 7; j++)
+ if (tgfx_base[i]->sticks & (1 << j))
+ input_unregister_device(tgfx_base[i]->dev + j);
+ parport_unregister_device(tgfx_base[i]->pd);
+ }
+}
+
+module_init(tgfx_init);
+module_exit(tgfx_exit);
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
new file mode 100644
index 000000000000..0379bc166525
--- /dev/null
+++ b/drivers/input/joystick/twidjoy.c
@@ -0,0 +1,296 @@
+/*
+ * $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>
+
+#define DRIVER_DESC "Handykey Twiddler keyboard as a joystick driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * 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, struct pt_regs *regs)
+{
+ 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);
+
+ input_regs(dev, regs);
+
+ 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);
+
+ input_sync(dev);
+ }
+
+ 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 irqreturn_t twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct twidjoy *twidjoy = serio_get_drvdata(serio);
+
+ /* 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 IRQ_HANDLED; /* 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, regs);
+ twidjoy->idx = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * twidjoy_disconnect() is the opposite of twidjoy_connect()
+ */
+
+static void twidjoy_disconnect(struct serio *serio)
+{
+ struct twidjoy *twidjoy = serio_get_drvdata(serio);
+
+ input_unregister_device(&twidjoy->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ 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 int twidjoy_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct twidjoy_button_spec *bp;
+ struct twidjoy *twidjoy;
+ int i;
+ int err;
+
+ if (!(twidjoy = kmalloc(sizeof(struct twidjoy), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(twidjoy, 0, sizeof(struct twidjoy));
+
+ sprintf(twidjoy->phys, "%s/input0", serio->phys);
+
+ init_input_dev(&twidjoy->dev);
+ twidjoy->dev.name = twidjoy_name;
+ twidjoy->dev.phys = twidjoy->phys;
+ twidjoy->dev.id.bustype = BUS_RS232;
+ twidjoy->dev.id.vendor = SERIO_TWIDJOY;
+ twidjoy->dev.id.product = 0x0001;
+ twidjoy->dev.id.version = 0x0100;
+ twidjoy->dev.dev = &serio->dev;
+
+ 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_set_drvdata(serio, twidjoy);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(twidjoy);
+ return err;
+ }
+
+ input_register_device(&twidjoy->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", twidjoy_name, serio->phys);
+
+ return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id twidjoy_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TWIDJOY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, twidjoy_serio_ids);
+
+static struct serio_driver twidjoy_drv = {
+ .driver = {
+ .name = "twidjoy",
+ },
+ .description = DRIVER_DESC,
+ .id_table = twidjoy_serio_ids,
+ .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_driver(&twidjoy_drv);
+ return 0;
+}
+
+void __exit twidjoy_exit(void)
+{
+ serio_unregister_driver(&twidjoy_drv);
+}
+
+module_init(twidjoy_init);
+module_exit(twidjoy_exit);
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
new file mode 100644
index 000000000000..6976a219504c
--- /dev/null
+++ b/drivers/input/joystick/warrior.c
@@ -0,0 +1,248 @@
+/*
+ * $Id: warrior.c,v 1.14 2002/01/22 20:32:10 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Logitech WingMan Warrior joystick driver for Linux
+ */
+
+/*
+ * This program is free warftware; 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/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Logitech WingMan Warrior joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define WARRIOR_MAX_LENGTH 16
+static char warrior_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 };
+static char *warrior_name = "Logitech WingMan Warrior";
+
+/*
+ * Per-Warrior data.
+ */
+
+struct warrior {
+ struct input_dev dev;
+ int idx, len;
+ unsigned char data[WARRIOR_MAX_LENGTH];
+ char phys[32];
+};
+
+/*
+ * warrior_process_packet() decodes packets the driver receives from the
+ * Warrior. It updates the data accordingly.
+ */
+
+static void warrior_process_packet(struct warrior *warrior, struct pt_regs *regs)
+{
+ struct input_dev *dev = &warrior->dev;
+ unsigned char *data = warrior->data;
+
+ if (!warrior->idx) return;
+
+ input_regs(dev, regs);
+
+ switch ((data[0] >> 4) & 7) {
+ case 1: /* Button data */
+ input_report_key(dev, BTN_TRIGGER, data[3] & 1);
+ input_report_key(dev, BTN_THUMB, (data[3] >> 1) & 1);
+ input_report_key(dev, BTN_TOP, (data[3] >> 2) & 1);
+ input_report_key(dev, BTN_TOP2, (data[3] >> 3) & 1);
+ break;
+ case 3: /* XY-axis info->data */
+ input_report_abs(dev, ABS_X, ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)));
+ input_report_abs(dev, ABS_Y, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
+ break;
+ case 5: /* Throttle, spinner, hat info->data */
+ input_report_abs(dev, ABS_THROTTLE, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
+ input_report_abs(dev, ABS_HAT0X, (data[3] & 2 ? 1 : 0) - (data[3] & 1 ? 1 : 0));
+ input_report_abs(dev, ABS_HAT0Y, (data[3] & 8 ? 1 : 0) - (data[3] & 4 ? 1 : 0));
+ input_report_rel(dev, REL_DIAL, (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5));
+ break;
+ }
+ input_sync(dev);
+}
+
+/*
+ * warrior_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 irqreturn_t warrior_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct warrior *warrior = serio_get_drvdata(serio);
+
+ if (data & 0x80) {
+ if (warrior->idx) warrior_process_packet(warrior, regs);
+ warrior->idx = 0;
+ warrior->len = warrior_lengths[(data >> 4) & 7];
+ }
+
+ if (warrior->idx < warrior->len)
+ warrior->data[warrior->idx++] = data;
+
+ if (warrior->idx == warrior->len) {
+ if (warrior->idx) warrior_process_packet(warrior, regs);
+ warrior->idx = 0;
+ warrior->len = 0;
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * warrior_disconnect() is the opposite of warrior_connect()
+ */
+
+static void warrior_disconnect(struct serio *serio)
+{
+ struct warrior *warrior = serio_get_drvdata(serio);
+
+ input_unregister_device(&warrior->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(warrior);
+}
+
+/*
+ * warrior_connect() is the routine that is called when someone adds a
+ * new serio device. It looks for the Warrior, and if found, registers
+ * it as an input device.
+ */
+
+static int warrior_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct warrior *warrior;
+ int i;
+ int err;
+
+ if (!(warrior = kmalloc(sizeof(struct warrior), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(warrior, 0, sizeof(struct warrior));
+
+ warrior->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
+ warrior->dev.keybit[LONG(BTN_TRIGGER)] = BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_TOP2);
+ warrior->dev.relbit[0] = BIT(REL_DIAL);
+ warrior->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y);
+
+ sprintf(warrior->phys, "%s/input0", serio->phys);
+
+ init_input_dev(&warrior->dev);
+ warrior->dev.name = warrior_name;
+ warrior->dev.phys = warrior->phys;
+ warrior->dev.id.bustype = BUS_RS232;
+ warrior->dev.id.vendor = SERIO_WARRIOR;
+ warrior->dev.id.product = 0x0001;
+ warrior->dev.id.version = 0x0100;
+ warrior->dev.dev = &serio->dev;
+
+ for (i = 0; i < 2; i++) {
+ warrior->dev.absmax[ABS_X+i] = -64;
+ warrior->dev.absmin[ABS_X+i] = 64;
+ warrior->dev.absflat[ABS_X+i] = 8;
+ }
+
+ warrior->dev.absmax[ABS_THROTTLE] = -112;
+ warrior->dev.absmin[ABS_THROTTLE] = 112;
+
+ for (i = 0; i < 2; i++) {
+ warrior->dev.absmax[ABS_HAT0X+i] = -1;
+ warrior->dev.absmin[ABS_HAT0X+i] = 1;
+ }
+
+ warrior->dev.private = warrior;
+
+ serio_set_drvdata(serio, warrior);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(warrior);
+ return err;
+ }
+
+ input_register_device(&warrior->dev);
+
+ printk(KERN_INFO "input: Logitech WingMan Warrior on %s\n", serio->phys);
+
+ return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id warrior_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_WARRIOR,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, warrior_serio_ids);
+
+static struct serio_driver warrior_drv = {
+ .driver = {
+ .name = "warrior",
+ },
+ .description = DRIVER_DESC,
+ .id_table = warrior_serio_ids,
+ .interrupt = warrior_interrupt,
+ .connect = warrior_connect,
+ .disconnect = warrior_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init warrior_init(void)
+{
+ serio_register_driver(&warrior_drv);
+ return 0;
+}
+
+static void __exit warrior_exit(void)
+{
+ serio_unregister_driver(&warrior_drv);
+}
+
+module_init(warrior_init);
+module_exit(warrior_exit);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
new file mode 100644
index 000000000000..e55dee390775
--- /dev/null
+++ b/drivers/input/keyboard/Kconfig
@@ -0,0 +1,185 @@
+#
+# Input core configuration
+#
+menuconfig INPUT_KEYBOARD
+ bool "Keyboards" if EMBEDDED || !X86
+ default y
+ help
+ Say Y here, and a list of supported keyboards will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_KEYBOARD
+
+config KEYBOARD_ATKBD
+ tristate "AT keyboard" if !PC
+ default y
+ select SERIO
+ select SERIO_LIBPS2
+ select SERIO_I8042 if PC
+ select SERIO_GSCPS2 if GSC
+ help
+ Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
+ you'll need this, unless you have a different type keyboard (USB, ADB
+ or other). This also works for AT and PS/2 keyboards connected over a
+ PS/2 to serial converter.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atkbd.
+
+config KEYBOARD_ATKBD_HP_KEYCODES
+ bool "Use HP keyboard scancodes"
+ depends on PARISC && KEYBOARD_ATKBD
+ default y
+ help
+ Say Y here if you have a PA-RISC machine and want to use an AT or
+ PS/2 keyboard, and your keyboard uses keycodes that are specific to
+ PA-RISC keyboards.
+
+ Say N if you use a standard keyboard.
+
+config KEYBOARD_ATKBD_RDI_KEYCODES
+ bool "Use PrecisionBook keyboard scancodes"
+ depends on KEYBOARD_ATKBD_HP_KEYCODES
+ default n
+ help
+ If you have an RDI PrecisionBook, say Y here if you want to use its
+ built-in keyboard (as opposed to an external keyboard).
+
+ The PrecisionBook has five keys that conflict with those used by most
+ AT and PS/2 keyboards. These are as follows:
+
+ PrecisionBook Standard AT or PS/2
+
+ F1 F12
+ Left Ctrl Left Alt
+ Caps Lock Left Ctrl
+ Right Ctrl Caps Lock
+ Left 102nd key (the key to the right of Left Shift)
+
+ If you say N here, and use the PrecisionBook keyboard, then each key
+ in the left-hand column will be interpreted as the corresponding key
+ in the right-hand column.
+
+ If you say Y here, and use an external keyboard, then each key in the
+ right-hand column will be interpreted as the key shown in the
+ left-hand column.
+
+config KEYBOARD_SUNKBD
+ tristate "Sun Type 4 and Type 5 keyboard"
+ select SERIO
+ help
+ 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.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sunkbd.
+
+config KEYBOARD_LKKBD
+ tristate "DECstation/VAXstation LK201/LK401 keyboard"
+ select SERIO
+ help
+ Say Y here if you want to use a LK201 or LK401 style serial
+ keyboard. This keyboard is also useable on PCs if you attach
+ it with the inputattach program. The connector pinout is
+ described within lkkbd.c.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lkkbd.
+
+config KEYBOARD_LOCOMO
+ tristate "LoCoMo Keyboard Support"
+ depends on SHARP_LOCOMO
+ help
+ Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
+
+ To compile this driver as a module, choose M here: the
+ module will be called locomokbd.
+
+config KEYBOARD_XTKBD
+ tristate "XT keyboard"
+ select SERIO
+ help
+ 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.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xtkbd.
+
+config KEYBOARD_NEWTON
+ tristate "Newton keyboard"
+ select SERIO
+ help
+ Say Y here if you have a Newton keyboard on a serial port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called newtonkbd.
+
+config KEYBOARD_CORGI
+ tristate "Corgi keyboard"
+ depends on PXA_SHARPSL
+ default y
+ help
+ Say Y here to enable the keyboard on the Sharp Zaurus SL-C7xx
+ series of PDAs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called corgikbd.
+
+config KEYBOARD_MAPLE
+ tristate "Maple bus keyboard"
+ depends on SH_DREAMCAST && MAPLE
+ help
+ Say Y here if you have a DreamCast console running Linux and have
+ a keyboard attached to its Maple bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called maple_keyb.
+
+config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+ help
+ Say Y here if you are running Linux on any AMIGA and have a keyboard
+ attached.
+
+ To compile this driver as a module, choose M here: the
+ module will be called amikbd.
+
+config KEYBOARD_HIL_OLD
+ tristate "HP HIL keyboard support (simple driver)"
+ depends on GSC
+ default y
+ help
+ The "Human Interface Loop" is a older, 8-channel USB-like
+ controller used in several Hewlett Packard models. This driver
+ was adapted from the one written for m68k/hp300, and implements
+ support for a keyboard attached to the HIL port, but not for
+ any other types of HIL input devices like mice or tablets.
+ However, it has been thoroughly tested and is stable.
+
+ If you want full HIL support including support for multiple
+ keyboards, mices and tablets, you have to enable the
+ "HP System Device Controller i8042 Support" in the input/serio
+ submenu.
+
+config KEYBOARD_HIL
+ tristate "HP HIL keyboard support"
+ depends on GSC
+ default y
+ select HP_SDC
+ select HIL_MLC
+ select SERIO
+ help
+ The "Human Interface Loop" is a older, 8-channel USB-like
+ controller used in several Hewlett Packard models.
+ This driver implements support for HIL-keyboards attached
+ to your machine, so normally you should say Y here.
+
+endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
new file mode 100644
index 000000000000..b02eeceea3c3
--- /dev/null
+++ b/drivers/input/keyboard/Makefile
@@ -0,0 +1,19 @@
+#
+# 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_SUNKBD) += sunkbd.o
+obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
+obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
+obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
+obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o
+obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
+obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
+obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
new file mode 100644
index 000000000000..4e8e8ea214ab
--- /dev/null
+++ b/drivers/input/keyboard/amikbd.c
@@ -0,0 +1,241 @@
+/*
+ * $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 <linux/delay.h>
+#include <linux/interrupt.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] = {
+ [0] = KEY_GRAVE,
+ [1] = KEY_1,
+ [2] = KEY_2,
+ [3] = KEY_3,
+ [4] = KEY_4,
+ [5] = KEY_5,
+ [6] = KEY_6,
+ [7] = KEY_7,
+ [8] = KEY_8,
+ [9] = KEY_9,
+ [10] = KEY_0,
+ [11] = KEY_MINUS,
+ [12] = KEY_EQUAL,
+ [13] = KEY_BACKSLASH,
+ [15] = KEY_KP0,
+ [16] = KEY_Q,
+ [17] = KEY_W,
+ [18] = KEY_E,
+ [19] = KEY_R,
+ [20] = KEY_T,
+ [21] = KEY_Y,
+ [22] = KEY_U,
+ [23] = KEY_I,
+ [24] = KEY_O,
+ [25] = KEY_P,
+ [26] = KEY_LEFTBRACE,
+ [27] = KEY_RIGHTBRACE,
+ [29] = KEY_KP1,
+ [30] = KEY_KP2,
+ [31] = KEY_KP3,
+ [32] = KEY_A,
+ [33] = KEY_S,
+ [34] = KEY_D,
+ [35] = KEY_F,
+ [36] = KEY_G,
+ [37] = KEY_H,
+ [38] = KEY_J,
+ [39] = KEY_K,
+ [40] = KEY_L,
+ [41] = KEY_SEMICOLON,
+ [42] = KEY_APOSTROPHE,
+ [43] = KEY_BACKSLASH,
+ [45] = KEY_KP4,
+ [46] = KEY_KP5,
+ [47] = KEY_KP6,
+ [48] = KEY_102ND,
+ [49] = KEY_Z,
+ [50] = KEY_X,
+ [51] = KEY_C,
+ [52] = KEY_V,
+ [53] = KEY_B,
+ [54] = KEY_N,
+ [55] = KEY_M,
+ [56] = KEY_COMMA,
+ [57] = KEY_DOT,
+ [58] = KEY_SLASH,
+ [60] = KEY_KPDOT,
+ [61] = KEY_KP7,
+ [62] = KEY_KP8,
+ [63] = KEY_KP9,
+ [64] = KEY_SPACE,
+ [65] = KEY_BACKSPACE,
+ [66] = KEY_TAB,
+ [67] = KEY_KPENTER,
+ [68] = KEY_ENTER,
+ [69] = KEY_ESC,
+ [70] = KEY_DELETE,
+ [74] = KEY_KPMINUS,
+ [76] = KEY_UP,
+ [77] = KEY_DOWN,
+ [78] = KEY_RIGHT,
+ [79] = KEY_LEFT,
+ [80] = KEY_F1,
+ [81] = KEY_F2,
+ [82] = KEY_F3,
+ [83] = KEY_F4,
+ [84] = KEY_F5,
+ [85] = KEY_F6,
+ [86] = KEY_F7,
+ [87] = KEY_F8,
+ [88] = KEY_F9,
+ [89] = KEY_F10,
+ [90] = KEY_KPLEFTPAREN,
+ [91] = KEY_KPRIGHTPAREN,
+ [92] = KEY_KPSLASH,
+ [93] = KEY_KPASTERISK,
+ [94] = KEY_KPPLUS,
+ [95] = KEY_HELP,
+ [96] = KEY_LEFTSHIFT,
+ [97] = KEY_RIGHTSHIFT,
+ [98] = KEY_CAPSLOCK,
+ [99] = KEY_LEFTCTRL,
+ [100] = KEY_LEFTALT,
+ [101] = KEY_RIGHTALT,
+ [102] = KEY_LEFTMETA,
+ [103] = KEY_RIGHTMETA
+};
+
+static const char *amikbd_messages[8] = {
+ [0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n",
+ [1] = KERN_WARNING "amikbd: keyboard lost sync\n",
+ [2] = KERN_WARNING "amikbd: keyboard buffer overflow\n",
+ [3] = KERN_WARNING "amikbd: keyboard controller failure\n",
+ [4] = KERN_ERR "amikbd: keyboard selftest failure\n",
+ [5] = KERN_INFO "amikbd: initiate power-up key stream\n",
+ [6] = KERN_INFO "amikbd: terminate power-up key stream\n",
+ [7] = 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 irqreturn_t 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 >>= 1;
+
+ if (scancode < 0x78) { /* scancodes < 0x78 are keys */
+
+ scancode = amikbd_keycode[scancode];
+
+ input_regs(&amikbd_dev, fp);
+
+ if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */
+ input_report_key(&amikbd_dev, scancode, 1);
+ input_report_key(&amikbd_dev, scancode, 0);
+ input_sync(&amikbd_dev);
+ } else {
+ input_report_key(&amikbd_dev, scancode, down);
+ input_sync(&amikbd_dev);
+ }
+ } else /* scancodes >= 0x78 are error codes */
+ printk(amikbd_messages[scancode - 0x78]);
+
+ return IRQ_HANDLED;
+}
+
+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;
+
+ init_input_dev(&amikbd_dev);
+
+ amikbd_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ amikbd_dev.keycode = amikbd_keycode;
+ amikbd_dev.keycodesize = sizeof(unsigned char);
+ amikbd_dev.keycodemax = ARRAY_SIZE(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", amikbd_interrupt);
+
+ amikbd_dev.name = amikbd_name;
+ amikbd_dev.phys = amikbd_phys;
+ amikbd_dev.id.bustype = BUS_AMIGA;
+ amikbd_dev.id.vendor = 0x0001;
+ amikbd_dev.id.product = 0x0001;
+ amikbd_dev.id.version = 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..f7304f0ce542
--- /dev/null
+++ b/drivers/input/keyboard/atkbd.c
@@ -0,0 +1,1148 @@
+/*
+ * AT and PS/2 keyboard driver
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This driver can handle standard AT keyboards and PS/2 keyboards in
+ * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb
+ * input-only controllers and AT keyboards connected over a one way RS232
+ * converter.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+#include <linux/libps2.h>
+
+#define DRIVER_DESC "AT and PS/2 keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static int atkbd_set = 2;
+module_param_named(set, atkbd_set, int, 0);
+MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)");
+
+#if defined(__i386__) || defined(__x86_64__) || defined(__hppa__)
+static int atkbd_reset;
+#else
+static int atkbd_reset = 1;
+#endif
+module_param_named(reset, atkbd_reset, bool, 0);
+MODULE_PARM_DESC(reset, "Reset keyboard during initialization");
+
+static int atkbd_softrepeat;
+module_param_named(softrepeat, atkbd_softrepeat, bool, 0);
+MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat");
+
+static int atkbd_softraw = 1;
+module_param_named(softraw, atkbd_softraw, bool, 0);
+MODULE_PARM_DESC(softraw, "Use software generated rawmode");
+
+static int atkbd_scroll = 1;
+module_param_named(scroll, atkbd_scroll, bool, 0);
+MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");
+
+static int atkbd_extra;
+module_param_named(extra, atkbd_extra, bool, 0);
+MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards");
+
+__obsolete_setup("atkbd_set=");
+__obsolete_setup("atkbd_reset");
+__obsolete_setup("atkbd_softrepeat=");
+
+/*
+ * Scancode to keycode tables. These are just the default setting, and
+ * are loadable via an userland utility.
+ */
+
+static unsigned char atkbd_set2_keycode[512] = {
+
+#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES
+
+/* XXX: need a more general approach */
+
+#include "hpps2atkbd.h" /* include the keyboard scancodes */
+
+#else
+ 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117,
+ 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0,
+ 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183,
+ 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185,
+ 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
+ 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85,
+ 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0,
+ 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125,
+ 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127,
+ 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142,
+ 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0,
+ 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112,
+ 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0,
+
+ 0, 0, 0, 65, 99,
+#endif
+};
+
+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, 43, 26, 13, 87, 99, 97, 54, 28, 27, 43, 43, 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,183,
+
+ 184,185,186,187, 74, 94, 92, 93, 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
+};
+
+static unsigned char atkbd_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
+};
+
+#define ATKBD_CMD_SETLEDS 0x10ed
+#define ATKBD_CMD_GSCANSET 0x11f0
+#define ATKBD_CMD_SSCANSET 0x10f0
+#define ATKBD_CMD_GETID 0x02f2
+#define ATKBD_CMD_SETREP 0x10f3
+#define ATKBD_CMD_ENABLE 0x00f4
+#define ATKBD_CMD_RESET_DIS 0x00f5
+#define ATKBD_CMD_SETALL_MBR 0x00fa
+#define ATKBD_CMD_RESET_BAT 0x02ff
+#define ATKBD_CMD_RESEND 0x00fe
+#define ATKBD_CMD_EX_ENABLE 0x10ea
+#define ATKBD_CMD_EX_SETLEDS 0x20eb
+#define ATKBD_CMD_OK_GETID 0x02e8
+
+#define ATKBD_RET_ACK 0xfa
+#define ATKBD_RET_NAK 0xfe
+#define ATKBD_RET_BAT 0xaa
+#define ATKBD_RET_EMUL0 0xe0
+#define ATKBD_RET_EMUL1 0xe1
+#define ATKBD_RET_RELEASE 0xf0
+#define ATKBD_RET_HANGUEL 0xf1
+#define ATKBD_RET_HANJA 0xf2
+#define ATKBD_RET_ERR 0xff
+
+#define ATKBD_KEY_UNKNOWN 0
+#define ATKBD_KEY_NULL 255
+
+#define ATKBD_SCR_1 254
+#define ATKBD_SCR_2 253
+#define ATKBD_SCR_4 252
+#define ATKBD_SCR_8 251
+#define ATKBD_SCR_CLICK 250
+#define ATKBD_SCR_LEFT 249
+#define ATKBD_SCR_RIGHT 248
+
+#define ATKBD_SPECIAL 248
+
+static struct {
+ unsigned char keycode;
+ unsigned char set2;
+} atkbd_scroll_keys[] = {
+ { ATKBD_SCR_1, 0xc5 },
+ { ATKBD_SCR_2, 0xa9 },
+ { ATKBD_SCR_4, 0xb6 },
+ { ATKBD_SCR_8, 0xa7 },
+ { ATKBD_SCR_CLICK, 0xe0 },
+ { ATKBD_SCR_LEFT, 0xcb },
+ { ATKBD_SCR_RIGHT, 0xd2 },
+};
+
+/*
+ * The atkbd control structure
+ */
+
+struct atkbd {
+
+ struct ps2dev ps2dev;
+
+ /* Written only during init */
+ char name[64];
+ char phys[32];
+ struct input_dev dev;
+
+ unsigned short id;
+ unsigned char keycode[512];
+ unsigned char set;
+ unsigned char translated;
+ unsigned char extra;
+ unsigned char write;
+ unsigned char softrepeat;
+ unsigned char softraw;
+ unsigned char scroll;
+ unsigned char enabled;
+
+ /* Accessed only from interrupt */
+ unsigned char emul;
+ unsigned char resend;
+ unsigned char release;
+ unsigned char bat_xl;
+ unsigned int last;
+ unsigned long time;
+};
+
+static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
+ ssize_t (*handler)(struct atkbd *, char *));
+static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
+ ssize_t (*handler)(struct atkbd *, const char *, size_t));
+#define ATKBD_DEFINE_ATTR(_name) \
+static ssize_t atkbd_show_##_name(struct atkbd *, char *); \
+static ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t); \
+static ssize_t atkbd_do_show_##_name(struct device *d, char *b) \
+{ \
+ return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \
+} \
+static ssize_t atkbd_do_set_##_name(struct device *d, const char *b, size_t s) \
+{ \
+ return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \
+} \
+static struct device_attribute atkbd_attr_##_name = \
+ __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name);
+
+ATKBD_DEFINE_ATTR(extra);
+ATKBD_DEFINE_ATTR(scroll);
+ATKBD_DEFINE_ATTR(set);
+ATKBD_DEFINE_ATTR(softrepeat);
+ATKBD_DEFINE_ATTR(softraw);
+
+
+static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
+{
+ input_regs(dev, regs);
+ if (value == 3) {
+ input_report_key(dev, code, 1);
+ input_sync(dev);
+ input_report_key(dev, code, 0);
+ } else
+ input_event(dev, EV_KEY, code, value);
+ input_sync(dev);
+}
+
+/*
+ * atkbd_interrupt(). Here takes place processing of data received from
+ * the keyboard into events.
+ */
+
+static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags, struct pt_regs *regs)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+ unsigned int code = data;
+ int scroll = 0, hscroll = 0, click = -1;
+ int value;
+
+#ifdef ATKBD_DEBUG
+ printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
+#endif
+
+#if !defined(__i386__) && !defined (__x86_64__)
+ if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
+ printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags);
+ serio_write(serio, ATKBD_CMD_RESEND);
+ atkbd->resend = 1;
+ goto out;
+ }
+
+ if (!flags && data == ATKBD_RET_ACK)
+ atkbd->resend = 0;
+#endif
+
+ if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK))
+ if (ps2_handle_ack(&atkbd->ps2dev, data))
+ goto out;
+
+ if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD))
+ if (ps2_handle_response(&atkbd->ps2dev, data))
+ goto out;
+
+ if (!atkbd->enabled)
+ goto out;
+
+ input_event(&atkbd->dev, EV_MSC, MSC_RAW, code);
+
+ if (atkbd->translated) {
+
+ if (atkbd->emul ||
+ !(code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1 ||
+ code == ATKBD_RET_HANGUEL || code == ATKBD_RET_HANJA ||
+ code == ATKBD_RET_ERR ||
+ (code == ATKBD_RET_BAT && !atkbd->bat_xl))) {
+ atkbd->release = code >> 7;
+ code &= 0x7f;
+ }
+
+ if (!atkbd->emul &&
+ (code & 0x7f) == (ATKBD_RET_BAT & 0x7f))
+ atkbd->bat_xl = !atkbd->release;
+ }
+
+ switch (code) {
+ case ATKBD_RET_BAT:
+ atkbd->enabled = 0;
+ serio_rescan(atkbd->ps2dev.serio);
+ goto out;
+ case ATKBD_RET_EMUL0:
+ atkbd->emul = 1;
+ goto out;
+ case ATKBD_RET_EMUL1:
+ atkbd->emul = 2;
+ goto out;
+ case ATKBD_RET_RELEASE:
+ atkbd->release = 1;
+ goto out;
+ case ATKBD_RET_HANGUEL:
+ atkbd_report_key(&atkbd->dev, regs, KEY_HANGUEL, 3);
+ goto out;
+ case ATKBD_RET_HANJA:
+ atkbd_report_key(&atkbd->dev, regs, KEY_HANJA, 3);
+ goto out;
+ case ATKBD_RET_ERR:
+ printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
+ goto out;
+ }
+
+ if (atkbd->set != 3)
+ code = (code & 0x7f) | ((code & 0x80) << 1);
+ if (atkbd->emul) {
+ if (--atkbd->emul)
+ goto out;
+ code |= (atkbd->set != 3) ? 0x80 : 0x100;
+ }
+
+ if (atkbd->keycode[code] != ATKBD_KEY_NULL)
+ input_event(&atkbd->dev, EV_MSC, MSC_SCAN, code);
+
+ switch (atkbd->keycode[code]) {
+ case ATKBD_KEY_NULL:
+ break;
+ case ATKBD_KEY_UNKNOWN:
+ if (data == ATKBD_RET_ACK || data == ATKBD_RET_NAK) {
+ printk(KERN_WARNING "atkbd.c: Spurious %s on %s. Some program, "
+ "like XFree86, might be trying access hardware directly.\n",
+ data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
+ } else {
+ printk(KERN_WARNING "atkbd.c: Unknown key %s "
+ "(%s set %d, code %#x on %s).\n",
+ atkbd->release ? "released" : "pressed",
+ atkbd->translated ? "translated" : "raw",
+ atkbd->set, code, serio->phys);
+ printk(KERN_WARNING "atkbd.c: Use 'setkeycodes %s%02x <keycode>' "
+ "to make it known.\n",
+ code & 0x80 ? "e0" : "", code & 0x7f);
+ }
+ input_sync(&atkbd->dev);
+ break;
+ case ATKBD_SCR_1:
+ scroll = 1 - atkbd->release * 2;
+ break;
+ case ATKBD_SCR_2:
+ scroll = 2 - atkbd->release * 4;
+ break;
+ case ATKBD_SCR_4:
+ scroll = 4 - atkbd->release * 8;
+ break;
+ case ATKBD_SCR_8:
+ scroll = 8 - atkbd->release * 16;
+ break;
+ case ATKBD_SCR_CLICK:
+ click = !atkbd->release;
+ break;
+ case ATKBD_SCR_LEFT:
+ hscroll = -1;
+ break;
+ case ATKBD_SCR_RIGHT:
+ hscroll = 1;
+ break;
+ default:
+ value = atkbd->release ? 0 :
+ (1 + (!atkbd->softrepeat && test_bit(atkbd->keycode[code], atkbd->dev.key)));
+
+ switch (value) { /* Workaround Toshiba laptop multiple keypress */
+ case 0:
+ atkbd->last = 0;
+ break;
+ case 1:
+ atkbd->last = code;
+ atkbd->time = jiffies + msecs_to_jiffies(atkbd->dev.rep[REP_DELAY]) / 2;
+ break;
+ case 2:
+ if (!time_after(jiffies, atkbd->time) && atkbd->last == code)
+ value = 1;
+ break;
+ }
+
+ atkbd_report_key(&atkbd->dev, regs, atkbd->keycode[code], value);
+ }
+
+ if (atkbd->scroll) {
+ input_regs(&atkbd->dev, regs);
+ if (click != -1)
+ input_report_key(&atkbd->dev, BTN_MIDDLE, click);
+ input_report_rel(&atkbd->dev, REL_WHEEL, scroll);
+ input_report_rel(&atkbd->dev, REL_HWHEEL, hscroll);
+ input_sync(&atkbd->dev);
+ }
+
+ atkbd->release = 0;
+out:
+ return IRQ_HANDLED;
+}
+
+/*
+ * 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;
+ const short period[32] =
+ { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125,
+ 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 };
+ const short delay[4] =
+ { 250, 500, 750, 1000 };
+ unsigned char param[2];
+ int i, j;
+
+ if (!atkbd->write)
+ return -1;
+
+ switch (type) {
+
+ case EV_LED:
+
+ param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
+ | (test_bit(LED_NUML, dev->led) ? 2 : 0)
+ | (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
+ ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS);
+
+ if (atkbd->extra) {
+ 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_MISC, dev->led) ? 0x10 : 0)
+ | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
+ ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS);
+ }
+
+ return 0;
+
+
+ case EV_REP:
+
+ if (atkbd->softrepeat) return 0;
+
+ i = j = 0;
+ while (i < 32 && period[i] < dev->rep[REP_PERIOD]) i++;
+ while (j < 4 && delay[j] < dev->rep[REP_DELAY]) j++;
+ dev->rep[REP_PERIOD] = period[i];
+ dev->rep[REP_DELAY] = delay[j];
+ param[0] = i | (j << 5);
+ ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * atkbd_enable() signals that interrupt handler is allowed to
+ * generate input events.
+ */
+
+static inline void atkbd_enable(struct atkbd *atkbd)
+{
+ serio_pause_rx(atkbd->ps2dev.serio);
+ atkbd->enabled = 1;
+ serio_continue_rx(atkbd->ps2dev.serio);
+}
+
+/*
+ * atkbd_disable() tells input handler that all incoming data except
+ * for ACKs and command response should be dropped.
+ */
+
+static inline void atkbd_disable(struct atkbd *atkbd)
+{
+ serio_pause_rx(atkbd->ps2dev.serio);
+ atkbd->enabled = 0;
+ serio_continue_rx(atkbd->ps2dev.serio);
+}
+
+/*
+ * atkbd_probe() probes for an AT keyboard on a serio port.
+ */
+
+static int atkbd_probe(struct atkbd *atkbd)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[2];
+
+/*
+ * Some systems, where the bit-twiddling when testing the io-lines of the
+ * controller may confuse the keyboard need a full reset of the keyboard. On
+ * these systems the BIOS also usually doesn't do it for us.
+ */
+
+ if (atkbd_reset)
+ if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_BAT))
+ printk(KERN_WARNING "atkbd.c: keyboard reset failed on %s\n", ps2dev->serio->phys);
+
+/*
+ * Then we check the keyboard ID. We should get 0xab83 under normal conditions.
+ * Some keyboards report different values, but the first byte is always 0xab or
+ * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this
+ * should make sure we don't try to set the LEDs on it.
+ */
+
+ param[0] = param[1] = 0xa5; /* initialize with invalid values */
+ if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) {
+
+/*
+ * If the get ID command failed, we check if we can at least set the LEDs on
+ * the keyboard. This should work on every keyboard out there. It also turns
+ * the LEDs off, which we want anyway.
+ */
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+ atkbd->id = 0xabba;
+ return 0;
+ }
+
+ if (param[0] != 0xab && param[0] != 0xac && /* Regular and NCD Sun keyboards */
+ param[0] != 0x2b && param[0] != 0x5d && /* Trust keyboard, raw and translated */
+ param[0] != 0x60 && param[0] != 0x47) /* NMB SGI keyboard, raw and translated */
+ return -1;
+
+ atkbd->id = (param[0] << 8) | param[1];
+
+ if (atkbd->id == 0xaca1 && atkbd->translated) {
+ printk(KERN_ERR "atkbd.c: NCD terminal keyboards are only supported on non-translating\n");
+ printk(KERN_ERR "atkbd.c: controllers. Use i8042.direct=1 to disable translation.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * atkbd_select_set 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_select_set(struct atkbd *atkbd, int target_set, int allow_extra)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[2];
+
+ atkbd->extra = 0;
+/*
+ * For known special keyboards we can go ahead and set the correct set.
+ * We check for NCD PS/2 Sun, NorthGate OmniKey 101 and
+ * IBM RapidAccess / IBM EzButton / Chicony KBP-8993 keyboards.
+ */
+
+ if (atkbd->translated)
+ return 2;
+
+ if (atkbd->id == 0xaca1) {
+ param[0] = 3;
+ ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET);
+ return 3;
+ }
+
+ if (allow_extra) {
+ param[0] = 0x71;
+ if (!ps2_command(ps2dev, param, ATKBD_CMD_EX_ENABLE)) {
+ atkbd->extra = 1;
+ return 2;
+ }
+ }
+
+ if (target_set != 3)
+ return 2;
+
+ if (!ps2_command(ps2dev, param, ATKBD_CMD_OK_GETID)) {
+ atkbd->id = param[0] << 8 | param[1];
+ return 2;
+ }
+
+ param[0] = 3;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
+ return 2;
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_GSCANSET))
+ return 2;
+
+ if (param[0] != 3) {
+ param[0] = 2;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
+ return 2;
+ }
+
+ ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR);
+
+ return 3;
+}
+
+static int atkbd_activate(struct atkbd *atkbd)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[1];
+
+/*
+ * Set the LEDs to a defined state.
+ */
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+
+/*
+ * Set autorepeat to fastest possible.
+ */
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP))
+ return -1;
+
+/*
+ * Enable the keyboard to receive keystrokes.
+ */
+
+ if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
+ printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n",
+ ps2dev->serio->phys);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
+ * reboot.
+ */
+
+static void atkbd_cleanup(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+ ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_BAT);
+}
+
+
+/*
+ * atkbd_disconnect() closes and frees.
+ */
+
+static void atkbd_disconnect(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+
+ atkbd_disable(atkbd);
+
+ /* make sure we don't have a command in flight */
+ synchronize_kernel();
+ flush_scheduled_work();
+
+ device_remove_file(&serio->dev, &atkbd_attr_extra);
+ device_remove_file(&serio->dev, &atkbd_attr_scroll);
+ device_remove_file(&serio->dev, &atkbd_attr_set);
+ device_remove_file(&serio->dev, &atkbd_attr_softrepeat);
+ device_remove_file(&serio->dev, &atkbd_attr_softraw);
+
+ input_unregister_device(&atkbd->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(atkbd);
+}
+
+
+/*
+ * atkbd_set_device_attrs() initializes keyboard's keycode table
+ * according to the selected scancode set
+ */
+
+static void atkbd_set_keycode_table(struct atkbd *atkbd)
+{
+ int i, j;
+
+ memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
+
+ if (atkbd->translated) {
+ for (i = 0; i < 128; i++) {
+ atkbd->keycode[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
+ atkbd->keycode[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
+ if (atkbd->scroll)
+ for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++)
+ if ((atkbd_unxlate_table[i] | 0x80) == atkbd_scroll_keys[j].set2)
+ atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode;
+ }
+ } else if (atkbd->set == 3) {
+ memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode));
+ } else {
+ memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));
+
+ if (atkbd->scroll)
+ for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++)
+ atkbd->keycode[atkbd_scroll_keys[i].set2] = atkbd_scroll_keys[i].keycode;
+ }
+}
+
+/*
+ * atkbd_set_device_attrs() sets up keyboard's input device structure
+ */
+
+static void atkbd_set_device_attrs(struct atkbd *atkbd)
+{
+ int i;
+
+ memset(&atkbd->dev, 0, sizeof(struct input_dev));
+
+ init_input_dev(&atkbd->dev);
+
+ atkbd->dev.name = atkbd->name;
+ atkbd->dev.phys = atkbd->phys;
+ atkbd->dev.id.bustype = BUS_I8042;
+ atkbd->dev.id.vendor = 0x0001;
+ atkbd->dev.id.product = atkbd->translated ? 1 : atkbd->set;
+ atkbd->dev.id.version = atkbd->id;
+ atkbd->dev.event = atkbd_event;
+ atkbd->dev.private = atkbd;
+ atkbd->dev.dev = &atkbd->ps2dev.serio->dev;
+
+ atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_MSC);
+
+ if (atkbd->write) {
+ atkbd->dev.evbit[0] |= BIT(EV_LED);
+ atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ }
+
+ if (atkbd->extra)
+ atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) |
+ BIT(LED_SLEEP) | BIT(LED_MUTE) | BIT(LED_MISC);
+
+ if (!atkbd->softrepeat) {
+ atkbd->dev.rep[REP_DELAY] = 250;
+ atkbd->dev.rep[REP_PERIOD] = 33;
+ }
+
+ atkbd->dev.mscbit[0] = atkbd->softraw ? BIT(MSC_SCAN) : BIT(MSC_RAW) | BIT(MSC_SCAN);
+
+ if (atkbd->scroll) {
+ atkbd->dev.evbit[0] |= BIT(EV_REL);
+ atkbd->dev.relbit[0] = BIT(REL_WHEEL) | BIT(REL_HWHEEL);
+ set_bit(BTN_MIDDLE, atkbd->dev.keybit);
+ }
+
+ atkbd->dev.keycode = atkbd->keycode;
+ atkbd->dev.keycodesize = sizeof(unsigned char);
+ atkbd->dev.keycodemax = ARRAY_SIZE(atkbd_set2_keycode);
+
+ for (i = 0; i < 512; i++)
+ if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL)
+ set_bit(atkbd->keycode[i], atkbd->dev.keybit);
+}
+
+/*
+ * atkbd_connect() is called when the serio module finds an 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 int atkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct atkbd *atkbd;
+ int err;
+
+ if (!(atkbd = kmalloc(sizeof(struct atkbd), GFP_KERNEL)))
+ return - ENOMEM;
+
+ memset(atkbd, 0, sizeof(struct atkbd));
+
+ ps2_init(&atkbd->ps2dev, serio);
+
+ switch (serio->id.type) {
+
+ case SERIO_8042_XL:
+ atkbd->translated = 1;
+ case SERIO_8042:
+ if (serio->write)
+ atkbd->write = 1;
+ break;
+ }
+
+ atkbd->softraw = atkbd_softraw;
+ atkbd->softrepeat = atkbd_softrepeat;
+ atkbd->scroll = atkbd_scroll;
+
+ if (!atkbd->write)
+ atkbd->softrepeat = 1;
+
+ if (atkbd->softrepeat)
+ atkbd->softraw = 1;
+
+ serio_set_drvdata(serio, atkbd);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(atkbd);
+ return err;
+ }
+
+ if (atkbd->write) {
+
+ if (atkbd_probe(atkbd)) {
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(atkbd);
+ return -ENODEV;
+ }
+
+ atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
+ atkbd_activate(atkbd);
+
+ } else {
+ atkbd->set = 2;
+ atkbd->id = 0xab00;
+ }
+
+ if (atkbd->extra)
+ sprintf(atkbd->name, "AT Set 2 Extra keyboard");
+ else
+ sprintf(atkbd->name, "AT %s Set %d keyboard",
+ atkbd->translated ? "Translated" : "Raw", atkbd->set);
+
+ sprintf(atkbd->phys, "%s/input0", serio->phys);
+
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ input_register_device(&atkbd->dev);
+
+ device_create_file(&serio->dev, &atkbd_attr_extra);
+ device_create_file(&serio->dev, &atkbd_attr_scroll);
+ device_create_file(&serio->dev, &atkbd_attr_set);
+ device_create_file(&serio->dev, &atkbd_attr_softrepeat);
+ device_create_file(&serio->dev, &atkbd_attr_softraw);
+
+ atkbd_enable(atkbd);
+
+ printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys);
+
+ return 0;
+}
+
+/*
+ * atkbd_reconnect() tries to restore keyboard into a sane state and is
+ * most likely called on resume.
+ */
+
+static int atkbd_reconnect(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+ struct serio_driver *drv = serio->drv;
+ unsigned char param[1];
+
+ if (!atkbd || !drv) {
+ printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ atkbd_disable(atkbd);
+
+ if (atkbd->write) {
+ param[0] = (test_bit(LED_SCROLLL, atkbd->dev.led) ? 1 : 0)
+ | (test_bit(LED_NUML, atkbd->dev.led) ? 2 : 0)
+ | (test_bit(LED_CAPSL, atkbd->dev.led) ? 4 : 0);
+
+ if (atkbd_probe(atkbd))
+ return -1;
+ if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
+ return -1;
+
+ atkbd_activate(atkbd);
+
+ if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+ }
+
+ atkbd_enable(atkbd);
+
+ return 0;
+}
+
+static struct serio_device_id atkbd_serio_ids[] = {
+ {
+ .type = SERIO_8042,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_8042_XL,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PS2SER,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, atkbd_serio_ids);
+
+static struct serio_driver atkbd_drv = {
+ .driver = {
+ .name = "atkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = atkbd_serio_ids,
+ .interrupt = atkbd_interrupt,
+ .connect = atkbd_connect,
+ .reconnect = atkbd_reconnect,
+ .disconnect = atkbd_disconnect,
+ .cleanup = atkbd_cleanup,
+};
+
+static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
+ ssize_t (*handler)(struct atkbd *, char *))
+{
+ struct serio *serio = to_serio_port(dev);
+ int retval;
+
+ retval = serio_pin_driver(serio);
+ if (retval)
+ return retval;
+
+ if (serio->drv != &atkbd_drv) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ retval = handler((struct atkbd *)serio_get_drvdata(serio), buf);
+
+out:
+ serio_unpin_driver(serio);
+ return retval;
+}
+
+static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
+ ssize_t (*handler)(struct atkbd *, const char *, size_t))
+{
+ struct serio *serio = to_serio_port(dev);
+ struct atkbd *atkbd;
+ int retval;
+
+ retval = serio_pin_driver(serio);
+ if (retval)
+ return retval;
+
+ if (serio->drv != &atkbd_drv) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ atkbd = serio_get_drvdata(serio);
+ atkbd_disable(atkbd);
+ retval = handler(atkbd, buf, count);
+ atkbd_enable(atkbd);
+
+out:
+ serio_unpin_driver(serio);
+ return retval;
+}
+
+static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->extra ? 1 : 0);
+}
+
+static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || value > 1)
+ return -EINVAL;
+
+ if (atkbd->extra != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->set = atkbd_select_set(atkbd, atkbd->set, value);
+ atkbd_activate(atkbd);
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0);
+}
+
+static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || value > 1)
+ return -EINVAL;
+
+ if (atkbd->scroll != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->scroll = value;
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->set);
+}
+
+static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || (value != 2 && value != 3))
+ return -EINVAL;
+
+ if (atkbd->set != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra);
+ atkbd_activate(atkbd);
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->softrepeat ? 1 : 0);
+}
+
+static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || value > 1)
+ return -EINVAL;
+
+ if (atkbd->softrepeat != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->softrepeat = value;
+ if (atkbd->softrepeat)
+ atkbd->softraw = 1;
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+
+ return count;
+}
+
+
+static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0);
+}
+
+static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || value > 1)
+ return -EINVAL;
+
+ if (atkbd->softraw != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->softraw = value;
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+ return count;
+}
+
+
+static int __init atkbd_init(void)
+{
+ serio_register_driver(&atkbd_drv);
+ return 0;
+}
+
+static void __exit atkbd_exit(void)
+{
+ serio_unregister_driver(&atkbd_drv);
+}
+
+module_init(atkbd_init);
+module_exit(atkbd_exit);
diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c
new file mode 100644
index 000000000000..0f1220a0ceb5
--- /dev/null
+++ b/drivers/input/keyboard/corgikbd.c
@@ -0,0 +1,361 @@
+/*
+ * Keyboard driver for Sharp Corgi models (SL-C7xx)
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on xtkbd.c/locomkbd.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/irq.h>
+
+#include <asm/arch/corgi.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/hardware/scoop.h>
+
+#define KB_ROWS 8
+#define KB_COLS 12
+#define KB_ROWMASK(r) (1 << (r))
+#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 )
+/* zero code, 124 scancodes + 3 hinge combinations */
+#define NR_SCANCODES ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 +3 )
+#define SCAN_INTERVAL (HZ/10)
+#define CORGIKBD_PRESSED 1
+
+#define HINGE_SCAN_INTERVAL (HZ/4)
+
+#define CORGI_KEY_CALENDER KEY_F1
+#define CORGI_KEY_ADDRESS KEY_F2
+#define CORGI_KEY_FN KEY_F3
+#define CORGI_KEY_OFF KEY_SUSPEND
+#define CORGI_KEY_EXOK KEY_F5
+#define CORGI_KEY_EXCANCEL KEY_F6
+#define CORGI_KEY_EXJOGDOWN KEY_F7
+#define CORGI_KEY_EXJOGUP KEY_F8
+#define CORGI_KEY_JAP1 KEY_LEFTCTRL
+#define CORGI_KEY_JAP2 KEY_LEFTALT
+#define CORGI_KEY_OK KEY_F11
+#define CORGI_KEY_MENU KEY_F12
+#define CORGI_HINGE_0 KEY_KP0
+#define CORGI_HINGE_1 KEY_KP1
+#define CORGI_HINGE_2 KEY_KP2
+
+static unsigned char corgikbd_keycode[NR_SCANCODES] = {
+ 0, /* 0 */
+ 0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0, /* 1-16 */
+ 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0, /* 17-32 */
+ KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */
+ CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */
+ CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, /* 65-80 */
+ KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */
+ KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */
+ CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */
+ CORGI_HINGE_0, CORGI_HINGE_1, CORGI_HINGE_2 /* 125-127 */
+};
+
+
+struct corgikbd {
+ unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)];
+ struct input_dev input;
+ char phys[32];
+
+ unsigned char state[ARRAY_SIZE(corgikbd_keycode)];
+ spinlock_t lock;
+
+ struct timer_list timer;
+ struct timer_list htimer;
+};
+
+static void handle_scancode(unsigned int pressed,unsigned int scancode, struct corgikbd *corgikbd_data)
+{
+ if (pressed && !(corgikbd_data->state[scancode] & CORGIKBD_PRESSED)) {
+ corgikbd_data->state[scancode] |= CORGIKBD_PRESSED;
+ input_report_key(&corgikbd_data->input, corgikbd_data->keycode[scancode], 1);
+ if (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF)
+ input_event(&corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1);
+ } else if (!pressed && corgikbd_data->state[scancode] & CORGIKBD_PRESSED) {
+ corgikbd_data->state[scancode] &= ~CORGIKBD_PRESSED;
+ input_report_key(&corgikbd_data->input, corgikbd_data->keycode[scancode], 0);
+ }
+}
+
+#define KB_DISCHARGE_DELAY 10
+#define KB_ACTIVATE_DELAY 10
+
+/* Helper functions for reading the keyboard matrix
+ * Note: We should really be using pxa_gpio_mode to alter GPDR but it
+ * requires a function call per GPIO bit which is excessive
+ * when we need to access 12 bits at once multiple times.
+ * These functions must be called within local_irq_save()/local_irq_restore()
+ * or similar.
+ */
+static inline void corgikbd_discharge_all(void)
+{
+ // STROBE All HiZ
+ GPCR2 = CORGI_GPIO_ALL_STROBE_BIT;
+ GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT;
+}
+
+static inline void corgikbd_activate_all(void)
+{
+ // STROBE ALL -> High
+ GPSR2 = CORGI_GPIO_ALL_STROBE_BIT;
+ GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT;
+
+ udelay(KB_DISCHARGE_DELAY);
+
+ // Clear any interrupts we may have triggered when altering the GPIO lines
+ GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT;
+ GEDR2 = CORGI_GPIO_LOW_SENSE_BIT;
+}
+
+static inline void corgikbd_activate_col(int col)
+{
+ // STROBE col -> High, not col -> HiZ
+ GPSR2 = CORGI_GPIO_STROBE_BIT(col);
+ GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
+}
+
+static inline void corgikbd_reset_col(int col)
+{
+ // STROBE col -> Low
+ GPCR2 = CORGI_GPIO_STROBE_BIT(col);
+ // STROBE col -> out, not col -> HiZ
+ GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
+}
+
+#define GET_ROWS_STATUS(c) (((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT))
+
+/*
+ * The corgi keyboard only generates interrupts when a key is pressed.
+ * When a key is pressed, we enable a timer which then scans the
+ * keyboard to detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data, struct pt_regs *regs)
+{
+ unsigned int row, col, rowd, scancode;
+ unsigned long flags;
+ unsigned int num_pressed;
+
+ spin_lock_irqsave(&corgikbd_data->lock, flags);
+
+ if (regs)
+ input_regs(&corgikbd_data->input, regs);
+
+ num_pressed = 0;
+ for (col = 0; col < KB_COLS; col++) {
+ /*
+ * Discharge the output driver capacitatance
+ * in the keyboard matrix. (Yes it is significant..)
+ */
+
+ corgikbd_discharge_all();
+ udelay(KB_DISCHARGE_DELAY);
+
+ corgikbd_activate_col(col);
+ udelay(KB_ACTIVATE_DELAY);
+
+ rowd = GET_ROWS_STATUS(col);
+ for (row = 0; row < KB_ROWS; row++) {
+ scancode = SCANCODE(row, col);
+ handle_scancode((rowd & KB_ROWMASK(row)), scancode, corgikbd_data);
+ if (rowd & KB_ROWMASK(row))
+ num_pressed++;
+ }
+ corgikbd_reset_col(col);
+ }
+
+ corgikbd_activate_all();
+
+ input_sync(&corgikbd_data->input);
+
+ /* if any keys are pressed, enable the timer */
+ if (num_pressed)
+ mod_timer(&corgikbd_data->timer, jiffies + SCAN_INTERVAL);
+
+ spin_unlock_irqrestore(&corgikbd_data->lock, flags);
+}
+
+/*
+ * corgi keyboard interrupt handler.
+ */
+static irqreturn_t corgikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct corgikbd *corgikbd_data = dev_id;
+
+ if (!timer_pending(&corgikbd_data->timer)) {
+ /** wait chattering delay **/
+ udelay(20);
+ corgikbd_scankeyboard(corgikbd_data, regs);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * corgi timer checking for released keys
+ */
+static void corgikbd_timer_callback(unsigned long data)
+{
+ struct corgikbd *corgikbd_data = (struct corgikbd *) data;
+ corgikbd_scankeyboard(corgikbd_data, NULL);
+}
+
+/*
+ * The hinge switches generate no interrupt so they need to be
+ * monitored by a timer.
+ *
+ * When we detect changes, we debounce it and then pass the three
+ * positions the system can take as keypresses to the input system.
+ */
+
+#define HINGE_STABLE_COUNT 2
+static int sharpsl_hinge_state;
+static int hinge_count;
+
+static void corgikbd_hinge_timer(unsigned long data)
+{
+ struct corgikbd *corgikbd_data = (struct corgikbd *) data;
+ unsigned long gprr;
+ unsigned long flags;
+
+ gprr = read_scoop_reg(SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB);
+ if (gprr != sharpsl_hinge_state) {
+ hinge_count = 0;
+ sharpsl_hinge_state = gprr;
+ } else if (hinge_count < HINGE_STABLE_COUNT) {
+ hinge_count++;
+ if (hinge_count >= HINGE_STABLE_COUNT) {
+ spin_lock_irqsave(&corgikbd_data->lock, flags);
+
+ handle_scancode((sharpsl_hinge_state == 0x00), 125, corgikbd_data); /* Keyboard with Landscape Screen */
+ handle_scancode((sharpsl_hinge_state == 0x08), 126, corgikbd_data); /* No Keyboard with Portrait Screen */
+ handle_scancode((sharpsl_hinge_state == 0x0c), 127, corgikbd_data); /* Keyboard and Screen Closed */
+ input_sync(&corgikbd_data->input);
+
+ spin_unlock_irqrestore(&corgikbd_data->lock, flags);
+ }
+ }
+ mod_timer(&corgikbd_data->htimer, jiffies + HINGE_SCAN_INTERVAL);
+}
+
+static int __init corgikbd_probe(struct device *dev)
+{
+ int i;
+ struct corgikbd *corgikbd;
+
+ corgikbd = kcalloc(1, sizeof(struct corgikbd), GFP_KERNEL);
+ if (!corgikbd)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev,corgikbd);
+ strcpy(corgikbd->phys, "corgikbd/input0");
+
+ spin_lock_init(corgikbd->lock);
+
+ /* Init Keyboard rescan timer */
+ init_timer(&corgikbd->timer);
+ corgikbd->timer.function = corgikbd_timer_callback;
+ corgikbd->timer.data = (unsigned long) corgikbd;
+
+ /* Init Hinge Timer */
+ init_timer(&corgikbd->htimer);
+ corgikbd->htimer.function = corgikbd_hinge_timer;
+ corgikbd->htimer.data = (unsigned long) corgikbd;
+
+ init_input_dev(&corgikbd->input);
+ corgikbd->input.private = corgikbd;
+ corgikbd->input.name = "Corgi Keyboard";
+ corgikbd->input.dev = dev;
+ corgikbd->input.phys = corgikbd->phys;
+ corgikbd->input.id.bustype = BUS_HOST;
+ corgikbd->input.id.vendor = 0x0001;
+ corgikbd->input.id.product = 0x0001;
+ corgikbd->input.id.version = 0x0100;
+ corgikbd->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR);
+ corgikbd->input.keycode = corgikbd->keycode;
+ corgikbd->input.keycodesize = sizeof(unsigned char);
+ corgikbd->input.keycodemax = ARRAY_SIZE(corgikbd_keycode);
+
+ memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode));
+ for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++)
+ set_bit(corgikbd->keycode[i], corgikbd->input.keybit);
+ clear_bit(0, corgikbd->input.keybit);
+
+ input_register_device(&corgikbd->input);
+ mod_timer(&corgikbd->htimer, jiffies + HINGE_SCAN_INTERVAL);
+
+ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
+ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
+ pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN);
+ if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt,
+ SA_INTERRUPT, "corgikbd", corgikbd))
+ printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i);
+ else
+ set_irq_type(CORGI_IRQ_GPIO_KEY_SENSE(i),IRQT_RISING);
+ }
+
+ /* Set Strobe lines as outputs - set high */
+ for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
+ pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
+
+ printk(KERN_INFO "input: Corgi Keyboard Registered\n");
+
+ return 0;
+}
+
+static int corgikbd_remove(struct device *dev)
+{
+ int i;
+ struct corgikbd *corgikbd = dev_get_drvdata(dev);
+
+ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++)
+ free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd);
+
+ del_timer_sync(&corgikbd->htimer);
+ del_timer_sync(&corgikbd->timer);
+
+ input_unregister_device(&corgikbd->input);
+
+ kfree(corgikbd);
+
+ return 0;
+}
+
+static struct device_driver corgikbd_driver = {
+ .name = "corgi-keyboard",
+ .bus = &platform_bus_type,
+ .probe = corgikbd_probe,
+ .remove = corgikbd_remove,
+};
+
+static int __devinit corgikbd_init(void)
+{
+ return driver_register(&corgikbd_driver);
+}
+
+static void __exit corgikbd_exit(void)
+{
+ driver_unregister(&corgikbd_driver);
+}
+
+module_init(corgikbd_init);
+module_exit(corgikbd_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Corgi Keyboard Driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
new file mode 100644
index 000000000000..ef78bffed5e7
--- /dev/null
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -0,0 +1,375 @@
+/*
+ * Generic linux-input device driver for keyboard devices
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
+ *
+ */
+
+#include <linux/hil.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+
+#define PREFIX "HIL KEYB: "
+#define HIL_GENERIC_NAME "HIL keyboard"
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define HIL_KBD_MAX_LENGTH 16
+
+#define HIL_KBD_SET1_UPBIT 0x01
+#define HIL_KBD_SET1_SHIFT 1
+static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] =
+ { HIL_KEYCODES_SET1 };
+
+#define HIL_KBD_SET2_UPBIT 0x01
+#define HIL_KBD_SET2_SHIFT 1
+/* Set2 is user defined */
+
+#define HIL_KBD_SET3_UPBIT 0x80
+#define HIL_KBD_SET3_SHIFT 0
+static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] =
+ { HIL_KEYCODES_SET3 };
+
+static char hil_language[][16] = { HIL_LOCALE_MAP };
+
+struct hil_kbd {
+ struct input_dev dev;
+ struct serio *serio;
+
+ /* Input buffer and index for packets from HIL bus. */
+ hil_packet data[HIL_KBD_MAX_LENGTH];
+ int idx4; /* four counts per packet */
+
+ /* Raw device info records from HIL bus, see hil.h for fields. */
+ char idd[HIL_KBD_MAX_LENGTH]; /* DID byte and IDD record */
+ char rsc[HIL_KBD_MAX_LENGTH]; /* RSC record */
+ char exd[HIL_KBD_MAX_LENGTH]; /* EXD record */
+ char rnm[HIL_KBD_MAX_LENGTH + 1]; /* RNM record + NULL term. */
+
+ /* Something to sleep around with. */
+ struct semaphore sem;
+};
+
+/* Process a complete packet after transfer from the HIL */
+static void hil_kbd_process_record(struct hil_kbd *kbd)
+{
+ struct input_dev *dev = &kbd->dev;
+ hil_packet *data = kbd->data;
+ hil_packet p;
+ int idx, i, cnt;
+
+ idx = kbd->idx4/4;
+ p = data[idx - 1];
+
+ if ((p & ~HIL_CMDCT_POL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report;
+ if ((p & ~HIL_CMDCT_RPL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report;
+
+ /* Not a poll response. See if we are loading config records. */
+ switch (p & HIL_PKT_DATA_MASK) {
+ case HIL_CMD_IDD:
+ for (i = 0; i < idx; i++)
+ kbd->idd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->idd[i] = 0;
+ break;
+ case HIL_CMD_RSC:
+ for (i = 0; i < idx; i++)
+ kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->rsc[i] = 0;
+ break;
+ case HIL_CMD_EXD:
+ for (i = 0; i < idx; i++)
+ kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->exd[i] = 0;
+ break;
+ case HIL_CMD_RNM:
+ for (i = 0; i < idx; i++)
+ kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH + 1; i++)
+ kbd->rnm[i] = '\0';
+ break;
+ default:
+ /* These occur when device isn't present */
+ if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break;
+ /* Anything else we'd like to know about. */
+ printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
+ break;
+ }
+ goto out;
+
+ report:
+ cnt = 1;
+ switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
+ case HIL_POL_CHARTYPE_NONE:
+ break;
+ case HIL_POL_CHARTYPE_ASCII:
+ while (cnt < idx - 1)
+ input_report_key(dev, kbd->data[cnt++] & 0x7f, 1);
+ break;
+ case HIL_POL_CHARTYPE_RSVD1:
+ case HIL_POL_CHARTYPE_RSVD2:
+ case HIL_POL_CHARTYPE_BINARY:
+ while (cnt < idx - 1)
+ input_report_key(dev, kbd->data[cnt++], 1);
+ break;
+ case HIL_POL_CHARTYPE_SET1:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET1_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT];
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+ case HIL_POL_CHARTYPE_SET2:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET2_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = key >> HIL_KBD_SET2_SHIFT;
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+ case HIL_POL_CHARTYPE_SET3:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET3_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT];
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+ }
+ out:
+ kbd->idx4 = 0;
+ up(&kbd->sem);
+}
+
+static void hil_kbd_process_err(struct hil_kbd *kbd) {
+ printk(KERN_WARNING PREFIX "errored HIL packet\n");
+ kbd->idx4 = 0;
+ up(&kbd->sem);
+}
+
+static irqreturn_t hil_kbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct hil_kbd *kbd;
+ hil_packet packet;
+ int idx;
+
+ kbd = (struct hil_kbd *)serio->private;
+ if (kbd == NULL) {
+ BUG();
+ return IRQ_HANDLED;
+ }
+
+ if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) {
+ hil_kbd_process_err(kbd);
+ return IRQ_HANDLED;
+ }
+ idx = kbd->idx4/4;
+ if (!(kbd->idx4 % 4)) kbd->data[idx] = 0;
+ packet = kbd->data[idx];
+ packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8);
+ kbd->data[idx] = packet;
+
+ /* Records of N 4-byte hil_packets must terminate with a command. */
+ if ((++(kbd->idx4)) % 4) return IRQ_HANDLED;
+ if ((packet & 0xffff0000) != HIL_ERR_INT) {
+ hil_kbd_process_err(kbd);
+ return IRQ_HANDLED;
+ }
+ if (packet & HIL_PKT_CMD) hil_kbd_process_record(kbd);
+ return IRQ_HANDLED;
+}
+
+static void hil_kbd_disconnect(struct serio *serio)
+{
+ struct hil_kbd *kbd;
+
+ kbd = (struct hil_kbd *)serio->private;
+ if (kbd == NULL) {
+ BUG();
+ return;
+ }
+
+ input_unregister_device(&kbd->dev);
+ serio_close(serio);
+ kfree(kbd);
+}
+
+static void hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct hil_kbd *kbd;
+ uint8_t did, *idd;
+ int i;
+
+ if (serio->type != (SERIO_HIL_MLC | SERIO_HIL)) return;
+
+ if (!(kbd = kmalloc(sizeof(struct hil_kbd), GFP_KERNEL))) return;
+ memset(kbd, 0, sizeof(struct hil_kbd));
+
+ if (serio_open(serio, drv)) goto bail0;
+
+ serio->private = kbd;
+ kbd->serio = serio;
+ kbd->dev.private = kbd;
+
+ init_MUTEX_LOCKED(&(kbd->sem));
+
+ /* Get device info. MLC driver supplies devid/status/etc. */
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_IDD);
+ down(&(kbd->sem));
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_RSC);
+ down(&(kbd->sem));
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_RNM);
+ down(&(kbd->sem));
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_EXD);
+ down(&(kbd->sem));
+
+ up(&(kbd->sem));
+
+ did = kbd->idd[0];
+ idd = kbd->idd + 1;
+ switch (did & HIL_IDD_DID_TYPE_MASK) {
+ case HIL_IDD_DID_TYPE_KB_INTEGRAL:
+ case HIL_IDD_DID_TYPE_KB_ITF:
+ case HIL_IDD_DID_TYPE_KB_RSVD:
+ case HIL_IDD_DID_TYPE_CHAR:
+ printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
+ did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
+ break;
+ default:
+ goto bail1;
+ }
+
+ if(HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
+ printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n");
+ goto bail1;
+ }
+
+
+ kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ kbd->dev.keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ kbd->dev.keycodesize = sizeof(hil_kbd_set1[0]);
+ kbd->dev.keycode = hil_kbd_set1;
+ kbd->dev.name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME;
+ kbd->dev.phys = "hpkbd/input0"; /* XXX */
+
+ kbd->dev.id.bustype = BUS_HIL;
+ kbd->dev.id.vendor = PCI_VENDOR_ID_HP;
+ kbd->dev.id.product = 0x0001; /* TODO: get from kbd->rsc */
+ kbd->dev.id.version = 0x0100; /* TODO: get from kbd->rsc */
+ kbd->dev.dev = &serio->dev;
+
+ for (i = 0; i < 128; i++) {
+ set_bit(hil_kbd_set1[i], kbd->dev.keybit);
+ set_bit(hil_kbd_set3[i], kbd->dev.keybit);
+ }
+ clear_bit(0, kbd->dev.keybit);
+
+ input_register_device(&kbd->dev);
+ printk(KERN_INFO "input: %s, ID: %d\n",
+ kbd->dev.name, did);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */
+ down(&(kbd->sem));
+ up(&(kbd->sem));
+
+ return;
+ bail1:
+ serio_close(serio);
+ bail0:
+ kfree(kbd);
+}
+
+
+struct serio_driver hil_kbd_serio_drv = {
+ .driver = {
+ .name = "hil_kbd",
+ },
+ .description = "HP HIL keyboard driver",
+ .connect = hil_kbd_connect,
+ .disconnect = hil_kbd_disconnect,
+ .interrupt = hil_kbd_interrupt
+};
+
+static int __init hil_kbd_init(void)
+{
+ serio_register_driver(&hil_kbd_serio_drv);
+ return 0;
+}
+
+static void __exit hil_kbd_exit(void)
+{
+ serio_unregister_driver(&hil_kbd_serio_drv);
+}
+
+module_init(hil_kbd_init);
+module_exit(hil_kbd_exit);
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
new file mode 100644
index 000000000000..eecb77db0847
--- /dev/null
+++ b/drivers/input/keyboard/hilkbd.c
@@ -0,0 +1,343 @@
+/*
+ * linux/drivers/hil/hilkbd.c
+ *
+ * Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ * Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
+ * Copyright (C) 1999-2003 Helge Deller <deller@gmx.de>
+ *
+ * Very basic HP Human Interface Loop (HIL) driver.
+ * This driver handles the keyboard on HP300 (m68k) and on some
+ * HP700 (parisc) series machines.
+ *
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/pci_ids.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/hil.h>
+#include <linux/spinlock.h>
+
+
+MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
+MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
+MODULE_LICENSE("GPL v2");
+
+
+#if defined(CONFIG_PARISC)
+
+ #include <asm/io.h>
+ #include <asm/hardware.h>
+ #include <asm/parisc-device.h>
+ static unsigned long hil_base; /* HPA for the HIL device */
+ static unsigned int hil_irq;
+ #define HILBASE hil_base /* HPPA (parisc) port address */
+ #define HIL_DATA 0x800
+ #define HIL_CMD 0x801
+ #define HIL_IRQ hil_irq
+ #define hil_readb(p) gsc_readb(p)
+ #define hil_writeb(v,p) gsc_writeb((v),(p))
+
+#elif defined(CONFIG_HP300)
+
+ #define HILBASE 0xf0428000 /* HP300 (m86k) port address */
+ #define HIL_DATA 0x1
+ #define HIL_CMD 0x3
+ #define HIL_IRQ 2
+ #define hil_readb(p) readb(p)
+ #define hil_writeb(v,p) writeb((v),(p))
+
+#else
+#error "HIL is not supported on this platform"
+#endif
+
+
+
+/* HIL helper functions */
+
+#define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
+#define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
+#define hil_status() (hil_readb(HILBASE + HIL_CMD))
+#define hil_command(x) do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
+#define hil_read_data() (hil_readb(HILBASE + HIL_DATA))
+#define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
+
+/* HIL constants */
+
+#define HIL_BUSY 0x02
+#define HIL_DATA_RDY 0x01
+
+#define HIL_SETARD 0xA0 /* set auto-repeat delay */
+#define HIL_SETARR 0xA2 /* set auto-repeat rate */
+#define HIL_SETTONE 0xA3 /* set tone generator */
+#define HIL_CNMT 0xB2 /* clear nmi */
+#define HIL_INTON 0x5C /* Turn on interrupts. */
+#define HIL_INTOFF 0x5D /* Turn off interrupts. */
+
+#define HIL_READKBDSADR 0xF9
+#define HIL_WRITEKBDSADR 0xE9
+
+static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] =
+ { HIL_KEYCODES_SET1 };
+
+/* HIL structure */
+static struct {
+ struct input_dev dev;
+
+ unsigned int curdev;
+
+ unsigned char s;
+ unsigned char c;
+ int valid;
+
+ unsigned char data[16];
+ unsigned int ptr;
+ spinlock_t lock;
+
+ void *dev_id; /* native bus device */
+} hil_dev;
+
+
+static void poll_finished(void)
+{
+ int down;
+ int key;
+ unsigned char scode;
+
+ switch (hil_dev.data[0]) {
+ case 0x40:
+ down = (hil_dev.data[1] & 1) == 0;
+ scode = hil_dev.data[1] >> 1;
+ key = hphilkeyb_keycode[scode];
+ input_report_key(&hil_dev.dev, key, down);
+ break;
+ }
+ hil_dev.curdev = 0;
+}
+
+static inline void handle_status(unsigned char s, unsigned char c)
+{
+ if (c & 0x8) {
+ /* End of block */
+ if (c & 0x10)
+ poll_finished();
+ } else {
+ if (c & 0x10) {
+ if (hil_dev.curdev)
+ poll_finished(); /* just in case */
+ hil_dev.curdev = c & 7;
+ hil_dev.ptr = 0;
+ }
+ }
+}
+
+static inline void handle_data(unsigned char s, unsigned char c)
+{
+ if (hil_dev.curdev) {
+ hil_dev.data[hil_dev.ptr++] = c;
+ hil_dev.ptr &= 15;
+ }
+}
+
+
+/*
+ * Handle HIL interrupts.
+ */
+static irqreturn_t hil_interrupt(int irq, void *handle, struct pt_regs *regs)
+{
+ unsigned char s, c;
+
+ s = hil_status();
+ c = hil_read_data();
+
+ switch (s >> 4) {
+ case 0x5:
+ handle_status(s, c);
+ break;
+ case 0x6:
+ handle_data(s, c);
+ break;
+ case 0x4:
+ hil_dev.s = s;
+ hil_dev.c = c;
+ mb();
+ hil_dev.valid = 1;
+ break;
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * Send a command to the HIL
+ */
+
+static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hil_dev.lock, flags);
+ while (hil_busy())
+ /* wait */;
+ hil_command(cmd);
+ while (len--) {
+ while (hil_busy())
+ /* wait */;
+ hil_write_data(*(data++));
+ }
+ spin_unlock_irqrestore(&hil_dev.lock, flags);
+}
+
+
+/*
+ * Initialise HIL.
+ */
+
+static int __init
+hil_keyb_init(void)
+{
+ unsigned char c;
+ unsigned int i, kbid;
+ wait_queue_head_t hil_wait;
+
+ if (hil_dev.dev.id.bustype) {
+ return -ENODEV; /* already initialized */
+ }
+
+#if defined(CONFIG_HP300)
+ if (!hwreg_present((void *)(HILBASE + HIL_DATA)))
+ return -ENODEV;
+
+ request_region(HILBASE+HIL_DATA, 2, "hil");
+#endif
+
+ request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
+
+ /* Turn on interrupts */
+ hil_do(HIL_INTON, NULL, 0);
+
+ /* Look for keyboards */
+ hil_dev.valid = 0; /* clear any pending data */
+ hil_do(HIL_READKBDSADR, NULL, 0);
+
+ init_waitqueue_head(&hil_wait);
+ wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ);
+ if (!hil_dev.valid) {
+ printk(KERN_WARNING "HIL: timed out, assuming no keyboard present.\n");
+ }
+
+ c = hil_dev.c;
+ hil_dev.valid = 0;
+ if (c == 0) {
+ kbid = -1;
+ printk(KERN_WARNING "HIL: no keyboard present.\n");
+ } else {
+ kbid = ffz(~c);
+ /* printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid); */
+ }
+
+ /* set it to raw mode */
+ c = 0;
+ hil_do(HIL_WRITEKBDSADR, &c, 1);
+
+ init_input_dev(&hil_dev.dev);
+
+ for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
+ if (hphilkeyb_keycode[i] != KEY_RESERVED)
+ set_bit(hphilkeyb_keycode[i], hil_dev.dev.keybit);
+
+ hil_dev.dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ hil_dev.dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ hil_dev.dev.keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ hil_dev.dev.keycodesize = sizeof(hphilkeyb_keycode[0]);
+ hil_dev.dev.keycode = hphilkeyb_keycode;
+ hil_dev.dev.name = "HIL keyboard";
+ hil_dev.dev.phys = "hpkbd/input0";
+
+ hil_dev.dev.id.bustype = BUS_HIL;
+ hil_dev.dev.id.vendor = PCI_VENDOR_ID_HP;
+ hil_dev.dev.id.product = 0x0001;
+ hil_dev.dev.id.version = 0x0010;
+
+ input_register_device(&hil_dev.dev);
+ printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
+ hil_dev.dev.name, kbid, HILBASE, HIL_IRQ);
+
+ return 0;
+}
+
+#if defined(CONFIG_PARISC)
+static int __init
+hil_init_chip(struct parisc_device *dev)
+{
+ if (!dev->irq) {
+ printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa);
+ return -ENODEV;
+ }
+
+ hil_base = dev->hpa;
+ hil_irq = dev->irq;
+ hil_dev.dev_id = dev;
+
+ printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
+
+ return hil_keyb_init();
+}
+
+static struct parisc_device_id hil_tbl[] = {
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, hil_tbl);
+
+static struct parisc_driver hil_driver = {
+ .name = "HIL",
+ .id_table = hil_tbl,
+ .probe = hil_init_chip,
+};
+#endif /* CONFIG_PARISC */
+
+
+
+
+
+static int __init hil_init(void)
+{
+#if defined(CONFIG_PARISC)
+ return register_parisc_driver(&hil_driver);
+#else
+ return hil_keyb_init();
+#endif
+}
+
+
+static void __exit hil_exit(void)
+{
+ if (HIL_IRQ) {
+ disable_irq(HIL_IRQ);
+ free_irq(HIL_IRQ, hil_dev.dev_id);
+ }
+
+ /* Turn off interrupts */
+ hil_do(HIL_INTOFF, NULL, 0);
+
+ input_unregister_device(&hil_dev.dev);
+
+#if defined(CONFIG_PARISC)
+ unregister_parisc_driver(&hil_driver);
+#else
+ release_region(HILBASE+HIL_DATA, 2);
+#endif
+}
+
+module_init(hil_init);
+module_exit(hil_exit);
+
diff --git a/drivers/input/keyboard/hpps2atkbd.h b/drivers/input/keyboard/hpps2atkbd.h
new file mode 100644
index 000000000000..dc33f6945222
--- /dev/null
+++ b/drivers/input/keyboard/hpps2atkbd.h
@@ -0,0 +1,110 @@
+/*
+ * drivers/input/keyboard/hpps2atkbd.h
+ *
+ * Copyright (c) 2004 Helge Deller <deller@gmx.de>
+ * Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
+ * Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
+ * Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
+ *
+ * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+
+/* Is the keyboard an RDI PrecisionBook? */
+#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES
+# define CONFLICT(x,y) x
+#else
+# define CONFLICT(x,y) y
+#endif
+
+/* sadly RDI (Tadpole) decided to ship a different keyboard layout
+ than HP for their PS/2 laptop keyboard which leads to conflicting
+ keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook.
+ HP: RDI: */
+#define C_07 CONFLICT( KEY_F12, KEY_F1 )
+#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL )
+#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK )
+#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL )
+#define C_61 CONFLICT( KEY_102ND, KEY_LEFT )
+
+/* Raw SET 2 scancode table */
+
+/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07,
+/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2,
+/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3,
+/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4,
+/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5,
+/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6,
+/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7,
+/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8,
+/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9,
+/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10,
+/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ,
+/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK,
+/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT,
+/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP,
+/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK,
+/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_102ND,
+/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 90 */ KEY_RESERVED, KEY_RIGHTALT, 255, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA,
+/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA,
+/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE,
+/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE,
+/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED,
+
+/* These are offset for escaped keycodes: */
+
+/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
+
+#undef CONFLICT
+#undef C_07
+#undef C_11
+#undef C_14
+#undef C_58
+#undef C_61
+
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
new file mode 100644
index 000000000000..2694ff2b5beb
--- /dev/null
+++ b/drivers/input/keyboard/lkkbd.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
+ */
+
+/*
+ * LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik
+ */
+
+/*
+ * DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations
+ * and VAXstations, but can also be used on any standard RS232 with an
+ * adaptor).
+ *
+ * DISCLAIMER: This works for _me_. If you break anything by using the
+ * information given below, I will _not_ be liable!
+ *
+ * RJ10 pinout: To DE9: Or DB25:
+ * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD)
+ * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND)
+ * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD)
+ * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!!
+ *
+ * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For
+ * RJ10, it's like this:
+ *
+ * __=__ Hold the plug in front of you, cable downwards,
+ * /___/| nose is hidden behind the plug. Now, pin 1 is at
+ * |1234|| the left side, pin 4 at the right and 2 and 3 are
+ * |IIII|| in between, of course:)
+ * | ||
+ * |____|/
+ * || So the adaptor consists of three connected cables
+ * || for data transmission (RxD and TxD) and signal ground.
+ * Additionally, you have to get +12V from somewhere.
+ * Most easily, you'll get that from a floppy or HDD power connector.
+ * It's the yellow cable there (black is ground and red is +5V).
+ *
+ * The keyboard and all the commands it understands are documented in
+ * "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This
+ * document is LK201 specific, but LK401 is mostly compatible. It comes
+ * up in LK201 mode and doesn't report any of the additional keys it
+ * has. These need to be switched on with the LK_CMD_ENABLE_LK401
+ * command. You'll find this document (scanned .pdf file) on MANX,
+ * a search engine specific to DEC documentation. Try
+ * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
+ */
+
+/*
+ * 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
+ * email or by paper mail:
+ * Jan-Benedict Glaw, Lilienstraße 16, 33790 Hörste (near Halle/Westf.),
+ * Germany.
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_DESC "LK keyboard driver"
+
+MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+
+/*
+ * Known parameters:
+ * bell_volume
+ * keyclick_volume
+ * ctrlclick_volume
+ *
+ * Please notice that there's not yet an API to set these at runtime.
+ */
+static int bell_volume = 100; /* % */
+module_param (bell_volume, int, 0);
+MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%");
+
+static int keyclick_volume = 100; /* % */
+module_param (keyclick_volume, int, 0);
+MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%");
+
+static int ctrlclick_volume = 100; /* % */
+module_param (ctrlclick_volume, int, 0);
+MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
+
+static int lk201_compose_is_alt = 0;
+module_param (lk201_compose_is_alt, int, 0);
+MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
+ "will act as an Alt key");
+
+
+
+#undef LKKBD_DEBUG
+#ifdef LKKBD_DEBUG
+#define DBG(x...) printk (x)
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+/* LED control */
+#define LK_LED_WAIT 0x81
+#define LK_LED_COMPOSE 0x82
+#define LK_LED_SHIFTLOCK 0x84
+#define LK_LED_SCROLLLOCK 0x88
+#define LK_CMD_LED_ON 0x13
+#define LK_CMD_LED_OFF 0x11
+
+/* Mode control */
+#define LK_MODE_DOWN 0x80
+#define LK_MODE_AUTODOWN 0x82
+#define LK_MODE_UPDOWN 0x86
+#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3))
+
+/* Misc commands */
+#define LK_CMD_ENABLE_KEYCLICK 0x1b
+#define LK_CMD_DISABLE_KEYCLICK 0x99
+#define LK_CMD_DISABLE_BELL 0xa1
+#define LK_CMD_SOUND_BELL 0xa7
+#define LK_CMD_ENABLE_BELL 0x23
+#define LK_CMD_DISABLE_CTRCLICK 0xb9
+#define LK_CMD_ENABLE_CTRCLICK 0xbb
+#define LK_CMD_SET_DEFAULTS 0xd3
+#define LK_CMD_POWERCYCLE_RESET 0xfd
+#define LK_CMD_ENABLE_LK401 0xe9
+#define LK_CMD_REQUEST_ID 0xab
+
+/* Misc responses from keyboard */
+#define LK_STUCK_KEY 0x3d
+#define LK_SELFTEST_FAILED 0x3e
+#define LK_ALL_KEYS_UP 0xb3
+#define LK_METRONOME 0xb4
+#define LK_OUTPUT_ERROR 0xb5
+#define LK_INPUT_ERROR 0xb6
+#define LK_KBD_LOCKED 0xb7
+#define LK_KBD_TEST_MODE_ACK 0xb8
+#define LK_PREFIX_KEY_DOWN 0xb9
+#define LK_MODE_CHANGE_ACK 0xba
+#define LK_RESPONSE_RESERVED 0xbb
+
+#define LK_NUM_KEYCODES 256
+#define LK_NUM_IGNORE_BYTES 6
+typedef u_int16_t lk_keycode_t;
+
+
+
+static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
+ [0x56] = KEY_F1,
+ [0x57] = KEY_F2,
+ [0x58] = KEY_F3,
+ [0x59] = KEY_F4,
+ [0x5a] = KEY_F5,
+ [0x64] = KEY_F6,
+ [0x65] = KEY_F7,
+ [0x66] = KEY_F8,
+ [0x67] = KEY_F9,
+ [0x68] = KEY_F10,
+ [0x71] = KEY_F11,
+ [0x72] = KEY_F12,
+ [0x73] = KEY_F13,
+ [0x74] = KEY_F14,
+ [0x7c] = KEY_F15,
+ [0x7d] = KEY_F16,
+ [0x80] = KEY_F17,
+ [0x81] = KEY_F18,
+ [0x82] = KEY_F19,
+ [0x83] = KEY_F20,
+ [0x8a] = KEY_FIND,
+ [0x8b] = KEY_INSERT,
+ [0x8c] = KEY_DELETE,
+ [0x8d] = KEY_SELECT,
+ [0x8e] = KEY_PAGEUP,
+ [0x8f] = KEY_PAGEDOWN,
+ [0x92] = KEY_KP0,
+ [0x94] = KEY_KPDOT,
+ [0x95] = KEY_KPENTER,
+ [0x96] = KEY_KP1,
+ [0x97] = KEY_KP2,
+ [0x98] = KEY_KP3,
+ [0x99] = KEY_KP4,
+ [0x9a] = KEY_KP5,
+ [0x9b] = KEY_KP6,
+ [0x9c] = KEY_KPCOMMA,
+ [0x9d] = KEY_KP7,
+ [0x9e] = KEY_KP8,
+ [0x9f] = KEY_KP9,
+ [0xa0] = KEY_KPMINUS,
+ [0xa1] = KEY_PROG1,
+ [0xa2] = KEY_PROG2,
+ [0xa3] = KEY_PROG3,
+ [0xa4] = KEY_PROG4,
+ [0xa7] = KEY_LEFT,
+ [0xa8] = KEY_RIGHT,
+ [0xa9] = KEY_DOWN,
+ [0xaa] = KEY_UP,
+ [0xab] = KEY_RIGHTSHIFT,
+ [0xac] = KEY_LEFTALT,
+ [0xad] = KEY_COMPOSE, /* Right Compose, that is. */
+ [0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */
+ [0xaf] = KEY_LEFTCTRL,
+ [0xb0] = KEY_CAPSLOCK,
+ [0xb1] = KEY_COMPOSE, /* Left Compose, that is. */
+ [0xb2] = KEY_RIGHTALT,
+ [0xbc] = KEY_BACKSPACE,
+ [0xbd] = KEY_ENTER,
+ [0xbe] = KEY_TAB,
+ [0xbf] = KEY_ESC,
+ [0xc0] = KEY_1,
+ [0xc1] = KEY_Q,
+ [0xc2] = KEY_A,
+ [0xc3] = KEY_Z,
+ [0xc5] = KEY_2,
+ [0xc6] = KEY_W,
+ [0xc7] = KEY_S,
+ [0xc8] = KEY_X,
+ [0xc9] = KEY_102ND,
+ [0xcb] = KEY_3,
+ [0xcc] = KEY_E,
+ [0xcd] = KEY_D,
+ [0xce] = KEY_C,
+ [0xd0] = KEY_4,
+ [0xd1] = KEY_R,
+ [0xd2] = KEY_F,
+ [0xd3] = KEY_V,
+ [0xd4] = KEY_SPACE,
+ [0xd6] = KEY_5,
+ [0xd7] = KEY_T,
+ [0xd8] = KEY_G,
+ [0xd9] = KEY_B,
+ [0xdb] = KEY_6,
+ [0xdc] = KEY_Y,
+ [0xdd] = KEY_H,
+ [0xde] = KEY_N,
+ [0xe0] = KEY_7,
+ [0xe1] = KEY_U,
+ [0xe2] = KEY_J,
+ [0xe3] = KEY_M,
+ [0xe5] = KEY_8,
+ [0xe6] = KEY_I,
+ [0xe7] = KEY_K,
+ [0xe8] = KEY_COMMA,
+ [0xea] = KEY_9,
+ [0xeb] = KEY_O,
+ [0xec] = KEY_L,
+ [0xed] = KEY_DOT,
+ [0xef] = KEY_0,
+ [0xf0] = KEY_P,
+ [0xf2] = KEY_SEMICOLON,
+ [0xf3] = KEY_SLASH,
+ [0xf5] = KEY_EQUAL,
+ [0xf6] = KEY_RIGHTBRACE,
+ [0xf7] = KEY_BACKSLASH,
+ [0xf9] = KEY_MINUS,
+ [0xfa] = KEY_LEFTBRACE,
+ [0xfb] = KEY_APOSTROPHE,
+};
+
+#define CHECK_LED(LED, BITS) do { \
+ if (test_bit (LED, lk->dev.led)) \
+ leds_on |= BITS; \
+ else \
+ leds_off |= BITS; \
+ } while (0)
+
+/*
+ * Per-keyboard data
+ */
+struct lkkbd {
+ lk_keycode_t keycode[LK_NUM_KEYCODES];
+ int ignore_bytes;
+ unsigned char id[LK_NUM_IGNORE_BYTES];
+ struct input_dev dev;
+ struct serio *serio;
+ struct work_struct tq;
+ char name[64];
+ char phys[32];
+ char type;
+ int bell_volume;
+ int keyclick_volume;
+ int ctrlclick_volume;
+};
+
+/*
+ * Calculate volume parameter byte for a given volume.
+ */
+static unsigned char
+volume_to_hw (int volume_percent)
+{
+ unsigned char ret = 0;
+
+ if (volume_percent < 0)
+ volume_percent = 0;
+ if (volume_percent > 100)
+ volume_percent = 100;
+
+ if (volume_percent >= 0)
+ ret = 7;
+ if (volume_percent >= 13) /* 12.5 */
+ ret = 6;
+ if (volume_percent >= 25)
+ ret = 5;
+ if (volume_percent >= 38) /* 37.5 */
+ ret = 4;
+ if (volume_percent >= 50)
+ ret = 3;
+ if (volume_percent >= 63) /* 62.5 */
+ ret = 2; /* This is the default volume */
+ if (volume_percent >= 75)
+ ret = 1;
+ if (volume_percent >= 88) /* 87.5 */
+ ret = 0;
+
+ ret |= 0x80;
+
+ return ret;
+}
+
+static void
+lkkbd_detection_done (struct lkkbd *lk)
+{
+ int i;
+
+ /*
+ * Reset setting for Compose key. Let Compose be KEY_COMPOSE.
+ */
+ lk->keycode[0xb1] = KEY_COMPOSE;
+
+ /*
+ * Print keyboard name and modify Compose=Alt on user's request.
+ */
+ switch (lk->id[4]) {
+ case 1:
+ sprintf (lk->name, "DEC LK201 keyboard");
+
+ if (lk201_compose_is_alt)
+ lk->keycode[0xb1] = KEY_LEFTALT;
+ break;
+
+ case 2:
+ sprintf (lk->name, "DEC LK401 keyboard");
+ break;
+
+ default:
+ sprintf (lk->name, "Unknown DEC keyboard");
+ printk (KERN_ERR "lkkbd: keyboard on %s is unknown, "
+ "please report to Jan-Benedict Glaw "
+ "<jbglaw@lug-owl.de>\n", lk->phys);
+ printk (KERN_ERR "lkkbd: keyboard ID'ed as:");
+ for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
+ printk (" 0x%02x", lk->id[i]);
+ printk ("\n");
+ break;
+ }
+ printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
+ lk->phys, lk->name);
+
+ /*
+ * Report errors during keyboard boot-up.
+ */
+ switch (lk->id[2]) {
+ case 0x00:
+ /* All okay */
+ break;
+
+ case LK_STUCK_KEY:
+ printk (KERN_ERR "lkkbd: Stuck key on keyboard at "
+ "%s\n", lk->phys);
+ break;
+
+ case LK_SELFTEST_FAILED:
+ printk (KERN_ERR "lkkbd: Selftest failed on keyboard "
+ "at %s, keyboard may not work "
+ "properly\n", lk->phys);
+ break;
+
+ default:
+ printk (KERN_ERR "lkkbd: Unknown error %02x on "
+ "keyboard at %s\n", lk->id[2],
+ lk->phys);
+ break;
+ }
+
+ /*
+ * Try to hint user if there's a stuck key.
+ */
+ if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
+ printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode "
+ "is 0x%04x\n", lk->id[3],
+ lk->keycode[lk->id[3]]);
+
+ return;
+}
+
+/*
+ * lkkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+static irqreturn_t
+lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags,
+ struct pt_regs *regs)
+{
+ struct lkkbd *lk = serio_get_drvdata (serio);
+ int i;
+
+ DBG (KERN_INFO "Got byte 0x%02x\n", data);
+
+ if (lk->ignore_bytes > 0) {
+ DBG (KERN_INFO "Ignoring a byte on %s\n",
+ lk->name);
+ lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+
+ if (lk->ignore_bytes == 0)
+ lkkbd_detection_done (lk);
+
+ return IRQ_HANDLED;
+ }
+
+ switch (data) {
+ case LK_ALL_KEYS_UP:
+ input_regs (&lk->dev, regs);
+ for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++)
+ if (lk->keycode[i] != KEY_RESERVED)
+ input_report_key (&lk->dev, lk->keycode[i], 0);
+ input_sync (&lk->dev);
+ break;
+ case LK_METRONOME:
+ DBG (KERN_INFO "Got LK_METRONOME and don't "
+ "know how to handle...\n");
+ break;
+ case LK_OUTPUT_ERROR:
+ DBG (KERN_INFO "Got LK_OUTPUT_ERROR and don't "
+ "know how to handle...\n");
+ break;
+ case LK_INPUT_ERROR:
+ DBG (KERN_INFO "Got LK_INPUT_ERROR and don't "
+ "know how to handle...\n");
+ break;
+ case LK_KBD_LOCKED:
+ DBG (KERN_INFO "Got LK_KBD_LOCKED and don't "
+ "know how to handle...\n");
+ break;
+ case LK_KBD_TEST_MODE_ACK:
+ DBG (KERN_INFO "Got LK_KBD_TEST_MODE_ACK and don't "
+ "know how to handle...\n");
+ break;
+ case LK_PREFIX_KEY_DOWN:
+ DBG (KERN_INFO "Got LK_PREFIX_KEY_DOWN and don't "
+ "know how to handle...\n");
+ break;
+ case LK_MODE_CHANGE_ACK:
+ DBG (KERN_INFO "Got LK_MODE_CHANGE_ACK and ignored "
+ "it properly...\n");
+ break;
+ case LK_RESPONSE_RESERVED:
+ DBG (KERN_INFO "Got LK_RESPONSE_RESERVED and don't "
+ "know how to handle...\n");
+ break;
+ case 0x01:
+ DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n");
+ lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
+ lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+ schedule_work (&lk->tq);
+ break;
+
+ default:
+ if (lk->keycode[data] != KEY_RESERVED) {
+ input_regs (&lk->dev, regs);
+ if (!test_bit (lk->keycode[data], lk->dev.key))
+ input_report_key (&lk->dev, lk->keycode[data], 1);
+ else
+ input_report_key (&lk->dev, lk->keycode[data], 0);
+ input_sync (&lk->dev);
+ } else
+ printk (KERN_WARNING "%s: Unknown key with "
+ "scancode 0x%02x on %s.\n",
+ __FILE__, data, lk->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * lkkbd_event() handles events from the input module.
+ */