Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
authorDmitry Torokhov <dtor@insightbb.com>
Tue, 19 Sep 2006 05:56:44 +0000 (01:56 -0400)
committerDmitry Torokhov <dtor@insightbb.com>
Tue, 19 Sep 2006 05:56:44 +0000 (01:56 -0400)
53 files changed:
Documentation/input/ff.txt
drivers/char/keyboard.c
drivers/input/Kconfig
drivers/input/Makefile
drivers/input/evbug.c
drivers/input/evdev.c
drivers/input/ff-core.c [new file with mode: 0644]
drivers/input/ff-memless.c [new file with mode: 0644]
drivers/input/fixp-arith.h [moved from drivers/usb/input/fixp-arith.h with 100% similarity]
drivers/input/input.c
drivers/input/joydev.c
drivers/input/joystick/iforce/iforce-ff.c
drivers/input/joystick/iforce/iforce-main.c
drivers/input/joystick/iforce/iforce-packets.c
drivers/input/joystick/iforce/iforce.h
drivers/input/keyboard/atkbd.c
drivers/input/misc/uinput.c
drivers/input/mouse/alps.c
drivers/input/mouse/alps.h
drivers/input/mouse/lifebook.c
drivers/input/mouse/logips2pp.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/sermouse.c
drivers/input/mouse/synaptics.c
drivers/input/mousedev.c
drivers/input/power.c
drivers/input/serio/i8042-x86ia64io.h
drivers/input/serio/i8042.c
drivers/input/serio/i8042.h
drivers/input/serio/libps2.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/elo.c
drivers/input/touchscreen/penmount.c [new file with mode: 0644]
drivers/input/touchscreen/touchright.c [new file with mode: 0644]
drivers/input/touchscreen/touchwin.c [new file with mode: 0644]
drivers/input/tsdev.c
drivers/usb/input/Kconfig
drivers/usb/input/Makefile
drivers/usb/input/hid-core.c
drivers/usb/input/hid-ff.c
drivers/usb/input/hid-input.c
drivers/usb/input/hid-lgff.c
drivers/usb/input/hid-pidff.c [new file with mode: 0644]
drivers/usb/input/hid-tmff.c
drivers/usb/input/hid-zpff.c [new file with mode: 0644]
drivers/usb/input/hid.h
drivers/usb/input/pid.c [deleted file]
drivers/usb/input/pid.h [deleted file]
include/linux/input.h
include/linux/libps2.h
include/linux/serio.h
include/linux/uinput.h

index c7e10eaff203c595e3ba99bd77540e7b86889723..c53b1c11aa40959f082579c490a3ffe994811d08 100644 (file)
@@ -1,67 +1,37 @@
 Force feedback for Linux.
 By Johann Deneux <deneux@ifrance.com> on 2001/04/22.
+Updated by Anssi Hannula <anssi.hannula@gmail.com> on 2006/04/09.
 You may redistribute this file. Please remember to include shape.fig and
 interactive.fig as well.
 ----------------------------------------------------------------------------
 
-0. Introduction
+1. Introduction
 ~~~~~~~~~~~~~~~
 This document describes how to use force feedback devices under Linux. The
 goal is not to support these devices as if they were simple input-only devices
 (as it is already the case), but to really enable the rendering of force
 effects.
-At the moment, only I-Force devices are supported, and not officially. That
-means I had to find out how the protocol works on my own. Of course, the
-information I managed to grasp is far from being complete, and I can not
-guarranty that this driver will work for you.
-This document only describes the force feedback part of the driver for I-Force
-devices. Please read joystick.txt before reading further this document.
+This document only describes the force feedback part of the Linux input
+interface. Please read joystick.txt and input.txt before reading further this
+document.
 
 2. Instructions to the user
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Here are instructions on how to compile and use the driver. In fact, this
-driver is the normal iforce, input and evdev drivers written by Vojtech
-Pavlik, plus additions to support force feedback.
+To enable force feedback, you have to:
+
+1. have your kernel configured with evdev and a driver that supports your
+   device.
+2. make sure evdev module is loaded and /dev/input/event* device files are
+   created.
 
 Before you start, let me WARN you that some devices shake violently during the
 initialisation phase. This happens for example with my "AVB Top Shot Pegasus".
 To stop this annoying behaviour, move you joystick to its limits. Anyway, you
-should keep a hand on your device, in order to avoid it to brake down if
+should keep a hand on your device, in order to avoid it to break down if
 something goes wrong.
 
-At the kernel's compilation:
-       - Enable IForce/Serial
-       - Enable Event interface
-
-Compile the modules, install them.
-
-You also need inputattach.
-
-You then need to insert the modules into the following order:
-% modprobe joydev
-% modprobe serport             # Only for serial
-% modprobe iforce
-% modprobe evdev
-% ./inputattach -ifor $2 &     # Only for serial
-If you are using USB, you don't need the inputattach step.
-
-Please check that you have all the /dev/input entries needed:
-cd /dev
-rm js*
-mkdir input
-mknod input/js0 c 13 0
-mknod input/js1 c 13 1
-mknod input/js2 c 13 2
-mknod input/js3 c 13 3
-ln -s input/js0 js0
-ln -s input/js1 js1
-ln -s input/js2 js2
-ln -s input/js3 js3
-
-mknod input/event0 c 13 64
-mknod input/event1 c 13 65
-mknod input/event2 c 13 66
-mknod input/event3 c 13 67
+If you have a serial iforce device, you need to start inputattach. See
+joystick.txt for details.
 
 2.1 Does it work ?
 ~~~~~~~~~~~~~~~~~~
@@ -70,9 +40,9 @@ There is an utility called fftest that will allow you to test the driver.
 
 3. Instructions to the developper
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  All interactions are done using the event API. That is, you can use ioctl()
+All interactions are done using the event API. That is, you can use ioctl()
 and write() on /dev/input/eventXX.
-  This information is subject to change.
+This information is subject to change.
 
 3.1 Querying device capabilities
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -86,18 +56,29 @@ int ioctl(int file_descriptor, int request, unsigned long *features);
 
 Returns the features supported by the device. features is a bitfield with the
 following bits:
-- FF_X         has an X axis (usually joysticks)
-- FF_Y         has an Y axis (usually joysticks)
-- FF_WHEEL     has a wheel (usually sterring wheels)
 - FF_CONSTANT  can render constant force effects
-- FF_PERIODIC  can render periodic effects (sine, triangle, square...)
+- FF_PERIODIC  can render periodic effects with the following waveforms:
+  - FF_SQUARE    square waveform
+  - FF_TRIANGLE          triangle waveform
+  - FF_SINE      sine waveform
+  - FF_SAW_UP    sawtooth up waveform
+  - FF_SAW_DOWN          sawtooth down waveform
+  - FF_CUSTOM    custom waveform
 - FF_RAMP       can render ramp effects
 - FF_SPRING    can simulate the presence of a spring
-- FF_FRICTION  can simulate friction 
+- FF_FRICTION  can simulate friction
 - FF_DAMPER    can simulate damper effects
-- FF_RUMBLE    rumble effects (normally the only effect supported by rumble
-               pads)
+- FF_RUMBLE    rumble effects
 - FF_INERTIA    can simulate inertia
+- FF_GAIN      gain is adjustable
+- FF_AUTOCENTER        autocenter is adjustable
+
+Note: In most cases you should use FF_PERIODIC instead of FF_RUMBLE. All
+      devices that support FF_RUMBLE support FF_PERIODIC (square, triangle,
+      sine) and the other way around.
+
+Note: The exact syntax FF_CUSTOM is undefined for the time being as no driver
+      supports it yet.
 
 
 int ioctl(int fd, EVIOCGEFFECTS, int *n);
@@ -108,7 +89,7 @@ Returns the number of effects the device can keep in its memory.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 #include <linux/input.h>
 #include <sys/ioctl.h>
+
 int ioctl(int file_descriptor, int request, struct ff_effect *effect);
 
 "request" must be EVIOCSFF.
@@ -120,6 +101,9 @@ to the unique id assigned by the driver. This data is required for performing
 some operations (removing an effect, controlling the playback).
 This if field must be set to -1 by the user in order to tell the driver to
 allocate a new effect.
+
+Effects are file descriptor specific.
+
 See <linux/input.h> for a description of the ff_effect struct. You should also
 find help in a few sketches, contained in files shape.fig and interactive.fig.
 You need xfig to visualize these files.
@@ -128,8 +112,8 @@ You need xfig to visualize these files.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 int ioctl(int fd, EVIOCRMFF, effect.id);
 
-This makes room for new effects in the device's memory. Please note this won't
-stop the effect if it was playing.
+This makes room for new effects in the device's memory. Note that this also
+stops the effect if it was playing.
 
 3.4 Controlling the playback of effects
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -149,22 +133,21 @@ Control of playing is done with write(). Below is an example:
        play.type = EV_FF;
        play.code = effect.id;
        play.value = 3;
-       
+
        write(fd, (const void*) &play, sizeof(play));
 ...
        /* Stop an effect */
        stop.type = EV_FF;
        stop.code = effect.id;
        stop.value = 0;
-       
+
        write(fd, (const void*) &play, sizeof(stop));
 
 3.5 Setting the gain
 ~~~~~~~~~~~~~~~~~~~~
 Not all devices have the same strength. Therefore, users should set a gain
 factor depending on how strong they want effects to be. This setting is
-persistent across access to the driver, so you should not care about it if
-you are writing games, as another utility probably already set this for you.
+persistent across access to the driver.
 
 /* Set the gain of the device
 int gain;              /* between 0 and 100 */
@@ -204,11 +187,14 @@ type of device, not all parameters can be dynamically updated. For example,
 the direction of an effect cannot be updated with iforce devices. In this
 case, the driver stops the effect, up-load it, and restart it.
 
+Therefore it is recommended to dynamically change direction while the effect
+is playing only when it is ok to restart the effect with a replay count of 1.
 
 3.8 Information about the status of effects
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Every time the status of an effect is changed, an event is sent. The values
 and meanings of the fields of the event are as follows:
+
 struct input_event {
 /* When the status of the effect changed */
        struct timeval time;
@@ -225,3 +211,9 @@ struct input_event {
 
 FF_STATUS_STOPPED      The effect stopped playing
 FF_STATUS_PLAYING      The effect started to play
+
+NOTE: Status feedback is only supported by iforce driver. If you have
+      a really good reason to use this, please contact
+      linux-joystick@atrey.karlin.mff.cuni.cz or anssi.hannula@gmail.com
+      so that support for it can be added to the rest of the drivers.
+
index 3e90aac3751032b74178a85fd31f5778f3bb7d29..ca234ce8004ab040bf4f797cda3b9ae292c4ed7a 100644 (file)
@@ -1285,7 +1285,7 @@ static void kbd_event(struct input_handle *handle, unsigned int event_type,
  */
 static struct input_handle *kbd_connect(struct input_handler *handler,
                                        struct input_dev *dev,
-                                       struct input_device_id *id)
+                                       const struct input_device_id *id)
 {
        struct input_handle *handle;
        int i;
@@ -1334,7 +1334,7 @@ static void kbd_start(struct input_handle *handle)
        tasklet_enable(&keyboard_tasklet);
 }
 
-static struct input_device_id kbd_ids[] = {
+static const struct input_device_id kbd_ids[] = {
        {
                 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
                 .evbit = { BIT(EV_KEY) },
@@ -1362,6 +1362,7 @@ static struct input_handler kbd_handler = {
 int __init kbd_init(void)
 {
        int i;
+       int error;
 
         for (i = 0; i < MAX_NR_CONSOLES; i++) {
                kbd_table[i].ledflagstate = KBD_DEFLEDS;
@@ -1373,7 +1374,9 @@ int __init kbd_init(void)
                kbd_table[i].kbdmode = VC_XLATE;
        }
 
-       input_register_handler(&kbd_handler);
+       error = input_register_handler(&kbd_handler);
+       if (error)
+               return error;
 
        tasklet_enable(&keyboard_tasklet);
        tasklet_schedule(&keyboard_tasklet);
index 58223b5d842a00059c9d6b0ee30a45417fd0961a..96232313b1b97fc277aeb4c67ebff59d205eb6aa 100644 (file)
@@ -24,6 +24,20 @@ config INPUT
 
 if INPUT
 
+config INPUT_FF_MEMLESS
+       tristate "Support for memoryless force-feedback devices"
+       default n
+       ---help---
+         Say Y here if you have memoryless force-feedback input device
+         such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual
+         Power 2, or similar. You will also need to enable hardware-specific
+         driver.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ff-memless.
+
 comment "Userland interfaces"
 
 config INPUT_MOUSEDEV
index 1a6ff4982f302e124b67ad71b844131f0dabef93..a005b1df5f1a57ddc32be556f8c503d8f4f7a4fa 100644 (file)
@@ -4,7 +4,11 @@
 
 # Each configuration option enables a list of files.
 
-obj-$(CONFIG_INPUT)            += input.o
+obj-$(CONFIG_INPUT)            += input-core.o
+input-core-objs := input.o ff-core.o
+
+obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
+
 obj-$(CONFIG_INPUT_MOUSEDEV)   += mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)     += joydev.o
 obj-$(CONFIG_INPUT_EVDEV)      += evdev.o
index 07358fb51b82aacf8e7cb1b9fbe9f2cc943eb455..5a9653c3128a0d90308e632683f2a2c9a6e077ec 100644 (file)
@@ -42,10 +42,12 @@ 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);
+       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)
+static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev,
+                                         const struct input_device_id *id)
 {
        struct input_handle *handle;
 
@@ -72,7 +74,7 @@ static void evbug_disconnect(struct input_handle *handle)
        kfree(handle);
 }
 
-static struct input_device_id evbug_ids[] = {
+static const struct input_device_id evbug_ids[] = {
        { .driver_info = 1 },   /* Matches all devices */
        { },                    /* Terminating zero entry */
 };
@@ -89,8 +91,7 @@ static struct input_handler evbug_handler = {
 
 static int __init evbug_init(void)
 {
-       input_register_handler(&evbug_handler);
-       return 0;
+       return input_register_handler(&evbug_handler);
 }
 
 static void __exit evbug_exit(void)
index 4bf48188cc9114cdce3a6a97de55351db1bf9ec3..6439f378f6cc9562f23ec5f8ee331f57bd898f45 100644 (file)
@@ -391,8 +391,10 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
        struct evdev *evdev = list->evdev;
        struct input_dev *dev = evdev->handle.dev;
        struct input_absinfo abs;
+       struct ff_effect effect;
        int __user *ip = (int __user *)p;
        int i, t, u, v;
+       int error;
 
        if (!evdev->exist)
                return -ENODEV;
@@ -460,27 +462,22 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
                        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 *)p)->id)))
-                                       return -EFAULT;
-                               return err;
-                       } else
-                               return -ENOSYS;
+                       if (copy_from_user(&effect, p, sizeof(effect)))
+                               return -EFAULT;
 
-               case EVIOCRMFF:
-                       if (!dev->erase_effect)
-                               return -ENOSYS;
+                       error = input_ff_upload(dev, &effect, file);
 
-                       return dev->erase_effect(dev, (int)(unsigned long) p);
+                       if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
+                               return -EFAULT;
+
+                       return error;
+
+               case EVIOCRMFF:
+                       return input_ff_erase(dev, (int)(unsigned long) p, file);
 
                case EVIOCGEFFECTS:
-                       if (put_user(dev->ff_effects_max, ip))
+                       i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
+                       if (put_user(i, ip))
                                return -EFAULT;
                        return 0;
 
@@ -604,7 +601,7 @@ static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned lon
 }
 #endif
 
-static struct file_operations evdev_fops = {
+static const struct file_operations evdev_fops = {
        .owner =        THIS_MODULE,
        .read =         evdev_read,
        .write =        evdev_write,
@@ -619,7 +616,8 @@ static struct file_operations evdev_fops = {
        .flush =        evdev_flush
 };
 
-static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev,
+                                         const struct input_device_id *id)
 {
        struct evdev *evdev;
        struct class_device *cdev;
@@ -669,6 +667,7 @@ static void evdev_disconnect(struct input_handle *handle)
        evdev->exist = 0;
 
        if (evdev->open) {
+               input_flush_device(handle, NULL);
                input_close_device(handle);
                wake_up_interruptible(&evdev->wait);
                list_for_each_entry(list, &evdev->list, node)
@@ -677,7 +676,7 @@ static void evdev_disconnect(struct input_handle *handle)
                evdev_free(evdev);
 }
 
-static struct input_device_id evdev_ids[] = {
+static const struct input_device_id evdev_ids[] = {
        { .driver_info = 1 },   /* Matches all devices */
        { },                    /* Terminating zero entry */
 };
@@ -696,8 +695,7 @@ static struct input_handler evdev_handler = {
 
 static int __init evdev_init(void)
 {
-       input_register_handler(&evdev_handler);
-       return 0;
+       return input_register_handler(&evdev_handler);
 }
 
 static void __exit evdev_exit(void)
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
new file mode 100644 (file)
index 0000000..35656ca
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ *  Force feedback support for Linux input subsystem
+ *
+ *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ *  Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * 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
+ */
+
+/* #define DEBUG */
+
+#define debug(format, arg...) pr_debug("ff-core: " format "\n", ## arg)
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+/*
+ * Check that the effect_id is a valid effect and whether the user
+ * is the owner
+ */
+static int check_effect_access(struct ff_device *ff, int effect_id,
+                               struct file *file)
+{
+       if (effect_id < 0 || effect_id >= ff->max_effects ||
+           !ff->effect_owners[effect_id])
+               return -EINVAL;
+
+       if (file && ff->effect_owners[effect_id] != file)
+               return -EACCES;
+
+       return 0;
+}
+
+/*
+ * Checks whether 2 effects can be combined together
+ */
+static inline int check_effects_compatible(struct ff_effect *e1,
+                                          struct ff_effect *e2)
+{
+       return e1->type == e2->type &&
+              (e1->type != FF_PERIODIC ||
+               e1->u.periodic.waveform == e2->u.periodic.waveform);
+}
+
+/*
+ * Convert an effect into compatible one
+ */
+static int compat_effect(struct ff_device *ff, struct ff_effect *effect)
+{
+       int magnitude;
+
+       switch (effect->type) {
+       case FF_RUMBLE:
+               if (!test_bit(FF_PERIODIC, ff->ffbit))
+                       return -EINVAL;
+
+               /*
+                * calculate manginude of sine wave as average of rumble's
+                * 2/3 of strong magnitude and 1/3 of weak magnitude
+                */
+               magnitude = effect->u.rumble.strong_magnitude / 3 +
+                           effect->u.rumble.weak_magnitude / 6;
+
+               effect->type = FF_PERIODIC;
+               effect->u.periodic.waveform = FF_SINE;
+               effect->u.periodic.period = 50;
+               effect->u.periodic.magnitude = max(magnitude, 0x7fff);
+               effect->u.periodic.offset = 0;
+               effect->u.periodic.phase = 0;
+               effect->u.periodic.envelope.attack_length = 0;
+               effect->u.periodic.envelope.attack_level = 0;
+               effect->u.periodic.envelope.fade_length = 0;
+               effect->u.periodic.envelope.fade_level = 0;
+
+               return 0;
+
+       default:
+               /* Let driver handle conversion */
+               return 0;
+       }
+}
+
+/**
+ * input_ff_upload() - upload effect into force-feedback device
+ * @dev: input device
+ * @effect: effect to be uploaded
+ * @file: owner of the effect
+ */
+int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
+                   struct file *file)
+{
+       struct ff_device *ff = dev->ff;
+       struct ff_effect *old;
+       int ret = 0;
+       int id;
+
+       if (!test_bit(EV_FF, dev->evbit))
+               return -ENOSYS;
+
+       if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
+           !test_bit(effect->type, dev->ffbit)) {
+               debug("invalid or not supported effect type in upload");
+               return -EINVAL;
+       }
+
+       if (effect->type == FF_PERIODIC &&
+           (effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
+            effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
+            !test_bit(effect->u.periodic.waveform, dev->ffbit))) {
+               debug("invalid or not supported wave form in upload");
+               return -EINVAL;
+       }
+
+       if (!test_bit(effect->type, ff->ffbit)) {
+               ret = compat_effect(ff, effect);
+               if (ret)
+                       return ret;
+       }
+
+       mutex_lock(&ff->mutex);
+
+       if (effect->id == -1) {
+               for (id = 0; id < ff->max_effects; id++)
+                    if (!ff->effect_owners[id])
+                       break;
+
+               if (id >= ff->max_effects) {
+                       ret = -ENOSPC;
+                       goto out;
+               }
+
+               effect->id = id;
+               old = NULL;
+
+       } else {
+               id = effect->id;
+
+               ret = check_effect_access(ff, id, file);
+               if (ret)
+                       goto out;
+
+               old = &ff->effects[id];
+
+               if (!check_effects_compatible(effect, old)) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       ret = ff->upload(dev, effect, old);
+       if (ret)
+               goto out;
+
+       ff->effects[id] = *effect;
+       ff->effect_owners[id] = file;
+
+ out:
+       mutex_unlock(&ff->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(input_ff_upload);
+
+/*
+ * Erases the effect if the requester is also the effect owner. The mutex
+ * should already be locked before calling this function.
+ */
+static int erase_effect(struct input_dev *dev, int effect_id,
+                       struct file *file)
+{
+       struct ff_device *ff = dev->ff;
+       int error;
+
+       error = check_effect_access(ff, effect_id, file);
+       if (error)
+               return error;
+
+       ff->playback(dev, effect_id, 0);
+
+       if (ff->erase) {
+               error = ff->erase(dev, effect_id);
+               if (error)
+                       return error;
+       }
+
+       ff->effect_owners[effect_id] = NULL;
+
+       return 0;
+}
+
+/**
+ * input_ff_erase - erase an effect from device
+ * @dev: input device to erase effect from
+ * @effect_id: id of the ffect to be erased
+ * @file: purported owner of the request
+ *
+ * This function erases a force-feedback effect from specified device.
+ * The effect will only be erased if it was uploaded through the same
+ * file handle that is requesting erase.
+ */
+int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)
+{
+       struct ff_device *ff = dev->ff;
+       int ret;
+
+       if (!test_bit(EV_FF, dev->evbit))
+               return -ENOSYS;
+
+       mutex_lock(&ff->mutex);
+       ret = erase_effect(dev, effect_id, file);
+       mutex_unlock(&ff->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(input_ff_erase);
+
+/*
+ * flush_effects - erase all effects owned by a file handle
+ */
+static int flush_effects(struct input_dev *dev, struct file *file)
+{
+       struct ff_device *ff = dev->ff;
+       int i;
+
+       debug("flushing now");
+
+       mutex_lock(&ff->mutex);
+
+       for (i = 0; i < ff->max_effects; i++)
+               erase_effect(dev, i, file);
+
+       mutex_unlock(&ff->mutex);
+
+       return 0;
+}
+
+/**
+ * input_ff_event() - generic handler for force-feedback events
+ * @dev: input device to send the effect to
+ * @type: event type (anything but EV_FF is ignored)
+ * @code: event code
+ * @value: event value
+ */
+int input_ff_event(struct input_dev *dev, unsigned int type,
+                  unsigned int code, int value)
+{
+       struct ff_device *ff = dev->ff;
+
+       if (type != EV_FF)
+               return 0;
+
+       mutex_lock(&ff->mutex);
+
+       switch (code) {
+       case FF_GAIN:
+               if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff)
+                       break;
+
+               ff->set_gain(dev, value);
+               break;
+
+       case FF_AUTOCENTER:
+               if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffff)
+                       break;
+
+               ff->set_autocenter(dev, value);
+               break;
+
+       default:
+               ff->playback(dev, code, value);
+               break;
+       }
+
+       mutex_unlock(&ff->mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_event);
+
+/**
+ * input_ff_create() - create force-feedback device
+ * @dev: input device supporting force-feedback
+ * @max_effects: maximum number of effects supported by the device
+ *
+ * This function allocates all necessary memory for a force feedback
+ * portion of an input device and installs all default handlers.
+ * @dev->ffbit should be already set up before calling this function.
+ * Once ff device is created you need to setup its upload, erase,
+ * playback and other handlers before registering input device
+ */
+int input_ff_create(struct input_dev *dev, int max_effects)
+{
+       struct ff_device *ff;
+       int i;
+
+       if (!max_effects) {
+               printk(KERN_ERR
+                      "ff-core: cannot allocate device without any effects\n");
+               return -EINVAL;
+       }
+
+       ff = kzalloc(sizeof(struct ff_device) +
+                    max_effects * sizeof(struct file *), GFP_KERNEL);
+       if (!ff)
+               return -ENOMEM;
+
+       ff->effects = kcalloc(max_effects, sizeof(struct ff_effect),
+                             GFP_KERNEL);
+       if (!ff->effects) {
+               kfree(ff);
+               return -ENOMEM;
+       }
+
+       ff->max_effects = max_effects;
+       mutex_init(&ff->mutex);
+
+       dev->ff = ff;
+       dev->flush = flush_effects;
+       dev->event = input_ff_event;
+       set_bit(EV_FF, dev->evbit);
+
+       /* Copy "true" bits into ff device bitmap */
+       for (i = 0; i <= FF_MAX; i++)
+               if (test_bit(i, dev->ffbit))
+                       set_bit(i, ff->ffbit);
+
+       /* we can emulate RUMBLE with periodic effects */
+       if (test_bit(FF_PERIODIC, ff->ffbit))
+               set_bit(FF_RUMBLE, dev->ffbit);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create);
+
+/**
+ * input_ff_free() - frees force feedback portion of input device
+ * @dev: input device supporintg force feedback
+ *
+ * This function is only needed in error path as input core will
+ * automatically free force feedback structures when device is
+ * destroyed.
+ */
+void input_ff_destroy(struct input_dev *dev)
+{
+       clear_bit(EV_FF, dev->evbit);
+       if (dev->ff) {
+               if (dev->ff->destroy)
+                       dev->ff->destroy(dev->ff);
+               kfree(dev->ff->private);
+               kfree(dev->ff);
+               dev->ff = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(input_ff_destroy);
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
new file mode 100644 (file)
index 0000000..cd8b729
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ *  Force feedback support for memoryless devices
+ *
+ *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ *  Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * 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
+ */
+
+/* #define DEBUG */
+
+#define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg)
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+
+#include "fixp-arith.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@gmail.com>");
+MODULE_DESCRIPTION("Force feedback support for memoryless devices");
+
+/* Number of effects handled with memoryless devices */
+#define FF_MEMLESS_EFFECTS     16
+
+/* Envelope update interval in ms */
+#define FF_ENVELOPE_INTERVAL   50
+
+#define FF_EFFECT_STARTED      0
+#define FF_EFFECT_PLAYING      1
+#define FF_EFFECT_ABORTING     2
+
+struct ml_effect_state {
+       struct ff_effect *effect;
+       unsigned long flags;    /* effect state (STARTED, PLAYING, etc) */
+       int count;              /* loop count of the effect */
+       unsigned long play_at;  /* start time */
+       unsigned long stop_at;  /* stop time */
+       unsigned long adj_at;   /* last time the effect was sent */
+};
+
+struct ml_device {
+       void *private;
+       struct ml_effect_state states[FF_MEMLESS_EFFECTS];
+       int gain;
+       struct timer_list timer;
+       spinlock_t timer_lock;
+       struct input_dev *dev;
+
+       int (*play_effect)(struct input_dev *dev, void *data,
+                          struct ff_effect *effect);
+};
+
+static const struct ff_envelope *get_envelope(const struct ff_effect *effect)
+{
+       static const struct ff_envelope empty_envelope;
+
+       switch (effect->type) {
+               case FF_PERIODIC:
+                       return &effect->u.periodic.envelope;
+               case FF_CONSTANT:
+                       return &effect->u.constant.envelope;
+               default:
+                       return &empty_envelope;
+       }
+}
+
+/*
+ * Check for the next time envelope requires an update on memoryless devices
+ */
+static unsigned long calculate_next_time(struct ml_effect_state *state)
+{
+       const struct ff_envelope *envelope = get_envelope(state->effect);
+       unsigned long attack_stop, fade_start, next_fade;
+
+       if (envelope->attack_length) {
+               attack_stop = state->play_at +
+                       msecs_to_jiffies(envelope->attack_length);
+               if (time_before(state->adj_at, attack_stop))
+                       return state->adj_at +
+                                       msecs_to_jiffies(FF_ENVELOPE_INTERVAL);
+       }
+
+       if (state->effect->replay.length) {
+               if (envelope->fade_length) {
+                       /* check when fading should start */
+                       fade_start = state->stop_at -
+                                       msecs_to_jiffies(envelope->fade_length);
+
+                       if (time_before(state->adj_at, fade_start))
+                               return fade_start;
+
+                       /* already fading, advance to next checkpoint */
+                       next_fade = state->adj_at +
+                                       msecs_to_jiffies(FF_ENVELOPE_INTERVAL);
+                       if (time_before(next_fade, state->stop_at))
+                               return next_fade;
+               }
+
+               return state->stop_at;
+       }
+
+       return state->play_at;
+}
+
+static void ml_schedule_timer(struct ml_device *ml)
+{
+       struct ml_effect_state *state;
+       unsigned long now = jiffies;
+       unsigned long earliest = 0;
+       unsigned long next_at;
+       int events = 0;
+       int i;
+
+       debug("calculating next timer");
+
+       for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
+
+               state = &ml->states[i];
+
+               if (!test_bit(FF_EFFECT_STARTED, &state->flags))
+                       continue;
+
+               if (test_bit(FF_EFFECT_PLAYING, &state->flags))
+                       next_at = calculate_next_time(state);
+               else
+                       next_at = state->play_at;
+
+               if (time_before_eq(now, next_at) &&
+                   (++events == 1 || time_before(next_at, earliest)))
+                       earliest = next_at;
+       }
+
+       if (!events) {
+               debug("no actions");
+               del_timer(&ml->timer);
+       } else {
+               debug("timer set");
+               mod_timer(&ml->timer, earliest);
+       }
+}
+
+/*
+ * Apply an envelope to a value
+ */
+static int apply_envelope(struct ml_effect_state *state, int value,
+                         struct ff_envelope *envelope)
+{
+       struct ff_effect *effect = state->effect;
+       unsigned long now = jiffies;
+       int time_from_level;
+       int time_of_envelope;
+       int envelope_level;
+       int difference;
+
+       if (envelope->attack_length &&
+           time_before(now,
+                       state->play_at + msecs_to_jiffies(envelope->attack_length))) {
+               debug("value = 0x%x, attack_level = 0x%x", value,
+                     envelope->attack_level);
+               time_from_level = jiffies_to_msecs(now - state->play_at);
+               time_of_envelope = envelope->attack_length;
+               envelope_level = min_t(__s16, envelope->attack_level, 0x7fff);
+
+       } else if (envelope->fade_length && effect->replay.length &&
+                  time_after(now,
+                             state->stop_at - msecs_to_jiffies(envelope->fade_length)) &&
+                  time_before(now, state->stop_at)) {
+               time_from_level = jiffies_to_msecs(state->stop_at - now);
+               time_of_envelope = envelope->fade_length;
+               envelope_level = min_t(__s16, envelope->fade_level, 0x7fff);
+       } else
+               return value;
+
+       difference = abs(value) - envelope_level;
+
+       debug("difference = %d", difference);
+       debug("time_from_level = 0x%x", time_from_level);
+       debug("time_of_envelope = 0x%x", time_of_envelope);
+
+       difference = difference * time_from_level / time_of_envelope;
+
+       debug("difference = %d", difference);
+
+       return value < 0 ?
+               -(difference + envelope_level) : (difference + envelope_level);
+}
+
+/*
+ * Return the type the effect has to be converted into (memless devices)
+ */
+static int get_compatible_type(struct ff_device *ff, int effect_type)
+{
+
+       if (test_bit(effect_type, ff->ffbit))
+               return effect_type;
+
+       if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit))
+               return FF_RUMBLE;
+
+       printk(KERN_ERR
+              "ff-memless: invalid type in get_compatible_type()\n");
+
+       return 0;
+}
+
+/*
+ * Combine two effects and apply gain.
+ */
+static void ml_combine_effects(struct ff_effect *effect,
+                              struct ml_effect_state *state,
+                              int gain)
+{
+       struct ff_effect *new = state->effect;
+       unsigned int strong, weak, i;
+       int x, y;
+       fixp_t level;
+
+       switch (new->type) {
+       case FF_CONSTANT:
+               i = new->direction * 360 / 0xffff;
+               level = fixp_new16(apply_envelope(state,
+                                       new->u.constant.level,
+                                       &new->u.constant.envelope));
+               x = fixp_mult(fixp_sin(i), level) * gain / 0xffff;
+               y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff;
+               /*
+                * here we abuse ff_ramp to hold x and y of constant force
+                * If in future any driver wants something else than x and y
+                * in s8, this should be changed to something more generic
+                */
+               effect->u.ramp.start_level =
+                       max(min(effect->u.ramp.start_level + x, 0x7f), -0x80);
+               effect->u.ramp.end_level =
+                       max(min(effect->u.ramp.end_level + y, 0x7f), -0x80);
+               break;
+
+       case FF_RUMBLE:
+               strong = new->u.rumble.strong_magnitude * gain / 0xffff;
+               weak = new->u.rumble.weak_magnitude * gain / 0xffff;
+               effect->u.rumble.strong_magnitude =
+                       min(strong + effect->u.rumble.strong_magnitude,
+                           0xffffU);
+               effect->u.rumble.weak_magnitude =
+                       min(weak + effect->u.rumble.weak_magnitude, 0xffffU);
+               break;
+
+       case FF_PERIODIC:
+               i = apply_envelope(state, abs(new->u.periodic.magnitude),
+                                  &new->u.periodic.envelope);
+
+               /* here we also scale it 0x7fff => 0xffff */
+               i = i * gain / 0x7fff;
+
+               effect->u.rumble.strong_magnitude =
+                       min(i + effect->u.rumble.strong_magnitude, 0xffffU);
+               effect->u.rumble.weak_magnitude =
+                       min(i + effect->u.rumble.weak_magnitude, 0xffffU);
+               break;
+
+       default:
+               printk(KERN_ERR "ff-memless: invalid type in ml_combine_effects()\n");
+               break;
+       }
+
+}
+
+
+/*
+ * Because memoryless devices have only one effect per effect type active
+ * at one time we have to combine multiple effects into one
+ */
+static int ml_get_combo_effect(struct ml_device *ml,
+                              unsigned long *effect_handled,
+                              struct ff_effect *combo_effect)
+{
+       struct ff_effect *effect;
+       struct ml_effect_state *state;
+       int effect_type;
+       int i;
+
+       memset(combo_effect, 0, sizeof(struct ff_effect));
+
+       for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
+               if (__test_and_set_bit(i, effect_handled))
+                       continue;
+
+               state = &ml->states[i];
+               effect = state->effect;
+
+               if (!test_bit(FF_EFFECT_STARTED, &state->flags))
+                       continue;
+
+               if (time_before(jiffies, state->play_at))
+                       continue;
+
+               /*
+                * here we have started effects that are either
+                * currently playing (and may need be aborted)
+                * or need to start playing.
+                */
+               effect_type = get_compatible_type(ml->dev->ff, effect->type);
+               if (combo_effect->type != effect_type) {
+                       if (combo_effect->type != 0) {
+                               __clear_bit(i, effect_handled);
+                               continue;
+                       }
+                       combo_effect->type = effect_type;
+               }
+
+               if (__test_and_clear_bit(FF_EFFECT_ABORTING, &state->flags)) {
+                       __clear_bit(FF_EFFECT_PLAYING, &state->flags);
+                       __clear_bit(FF_EFFECT_STARTED, &state->flags);
+               } else if (effect->replay.length &&
+                          time_after_eq(jiffies, state->stop_at)) {
+
+                       __clear_bit(FF_EFFECT_PLAYING, &state->flags);
+
+                       if (--state->count <= 0) {
+                               __clear_bit(FF_EFFECT_STARTED, &state->flags);
+                       } else {
+                               state->play_at = jiffies +
+                                       msecs_to_jiffies(effect->replay.delay);
+                               state->stop_at = state->play_at +
+                                       msecs_to_jiffies(effect->replay.length);
+                       }
+               } else {
+                       __set_bit(FF_EFFECT_PLAYING, &state->flags);
+                       state->adj_at = jiffies;
+                       ml_combine_effects(combo_effect, state, ml->gain);
+               }
+       }
+
+       return combo_effect->type != 0;
+}
+
+static void ml_play_effects(struct ml_device *ml)
+{
+       struct ff_effect effect;
+       DECLARE_BITMAP(handled_bm, FF_MEMLESS_EFFECTS);
+
+       memset(handled_bm, 0, sizeof(handled_bm));
+
+       while (ml_get_combo_effect(ml, handled_bm, &effect))
+               ml->play_effect(ml->dev, ml->private, &effect);
+
+       ml_schedule_timer(ml);
+}
+
+static void ml_effect_timer(unsigned long timer_data)
+{
+       struct input_dev *dev = (struct input_dev *)timer_data;
+       struct ml_device *ml = dev->ff->private;
+
+       debug("timer: updating effects");
+
+       spin_lock(&ml->timer_lock);
+       ml_play_effects(ml);
+       spin_unlock(&ml->timer_lock);
+}
+
+static void ml_ff_set_gain(struct input_dev *dev, u16 gain)
+{
+       struct ml_device *ml = dev->ff->private;
+       int i;
+
+       spin_lock_bh(&ml->timer_lock);
+
+       ml->gain = gain;
+
+       for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
+               __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags);
+
+       ml_play_effects(ml);
+
+       spin_unlock_bh(&ml->timer_lock);
+}
+
+static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
+{
+       struct ml_device *ml = dev->ff->private;
+       struct ml_effect_state *state = &ml->states[effect_id];
+
+       spin_lock_bh(&ml->timer_lock);
+
+       if (value > 0) {
+               debug("initiated play");
+
+               __set_bit(FF_EFFECT_STARTED, &state->flags);
+               state->count = value;
+               state->play_at = jiffies +
+                                msecs_to_jiffies(state->effect->replay.delay);
+               state->stop_at = state->play_at +
+                                msecs_to_jiffies(state->effect->replay.length);
+               state->adj_at = state->play_at;
+
+               ml_schedule_timer(ml);
+
+       } else {
+               debug("initiated stop");
+
+               if (test_bit(FF_EFFECT_PLAYING, &state->flags))
+                       __set_bit(FF_EFFECT_ABORTING, &state->flags);
+               else
+                       __clear_bit(FF_EFFECT_STARTED, &state->flags);
+
+               ml_play_effects(ml);
+       }
+
+       spin_unlock_bh(&ml->timer_lock);
+
+       return 0;
+}
+
+static int ml_ff_upload(struct input_dev *dev,
+                       struct ff_effect *effect, struct ff_effect *old)
+{
+       struct ml_device *ml = dev->ff->private;
+       struct ml_effect_state *state = &ml->states[effect->id];
+
+       spin_lock_bh(&ml->timer_lock);
+
+       if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
+               __clear_bit(FF_EFFECT_PLAYING, &state->flags);
+               state->play_at = jiffies +
+                                msecs_to_jiffies(state->effect->replay.delay);
+               state->stop_at = state->play_at +
+                                msecs_to_jiffies(state->effect->replay.length);
+               state->adj_at = state->play_at;
+               ml_schedule_timer(ml);
+       }
+
+       spin_unlock_bh(&ml->timer_lock);
+
+       return 0;
+}
+
+static void ml_ff_destroy(struct ff_device *ff)
+{
+       struct ml_device *ml = ff->private;
+
+       kfree(ml->private);
+}
+
+/**
+ * input_ff_create_memless() - create memoryless FF device
+ * @dev: input device supporting force-feedback
+ * @data: driver-specific data to be passed into @play_effect
+ * @play_effect: driver-specific method for playing FF effect
+ */
+int input_ff_create_memless(struct input_dev *dev, void *data,
+               int (*play_effect)(struct input_dev *, void *, struct ff_effect *))
+{
+       struct ml_device *ml;
+       struct ff_device *ff;
+       int error;
+       int i;
+
+       ml = kzalloc(sizeof(struct ml_device), GFP_KERNEL);
+       if (!ml)
+               return -ENOMEM;
+
+       ml->dev = dev;
+       ml->private = data;
+       ml->play_effect = play_effect;
+       ml->gain = 0xffff;
+       spin_lock_init(&ml->timer_lock);
+       setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev);
+
+       set_bit(FF_GAIN, dev->ffbit);
+
+       error = input_ff_create(dev, FF_MEMLESS_EFFECTS);
+       if (error) {
+               kfree(ml);
+               return error;
+       }
+
+       ff = dev->ff;
+       ff->private = ml;
+       ff->upload = ml_ff_upload;
+       ff->playback = ml_ff_playback;
+       ff->set_gain = ml_ff_set_gain;
+       ff->destroy = ml_ff_destroy;
+
+       /* we can emulate periodic effects with RUMBLE */
+       if (test_bit(FF_RUMBLE, ff->ffbit)) {
+               set_bit(FF_PERIODIC, dev->ffbit);
+               set_bit(FF_SINE, dev->ffbit);
+               set_bit(FF_TRIANGLE, dev->ffbit);
+               set_bit(FF_SQUARE, dev->ffbit);
+       }
+
+       for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
+               ml->states[i].effect = &ff->effects[i];
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create_memless);
index 9cb4b9a54f01ffa38dd0f803785f190dae1f8adb..1c8c8a5bc4a91c0076c9af4539ef3a768102afed 100644 (file)
@@ -176,6 +176,10 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in
                        break;
 
                case EV_FF:
+
+                       if (value < 0)
+                               return;
+
                        if (dev->event)
                                dev->event(dev, type, code, value);
                        break;
@@ -309,7 +313,8 @@ static void input_link_handle(struct input_handle *handle)
                if (i != NBITS(max)) \
                        continue;
 
-static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
+static const struct input_device_id *input_match_device(const struct input_device_id *id,
+                                                       struct input_dev *dev)
 {
        int i;
 
@@ -762,7 +767,9 @@ static void input_dev_release(struct class_device *class_dev)
 {
        struct input_dev *dev = to_input_dev(class_dev);
 
+       input_ff_destroy(dev);
        kfree(dev);
+
        module_put(THIS_MODULE);
 }
 
@@ -899,12 +906,13 @@ struct input_dev *input_allocate_device(void)
 
        dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
        if (dev) {
-               dev->dynalloc = 1;
                dev->cdev.class = &input_class;
                class_device_initialize(&dev->cdev);
                mutex_init(&dev->mutex);
                INIT_LIST_HEAD(&dev->h_list);
                INIT_LIST_HEAD(&dev->node);
+
+               __module_get(THIS_MODULE);
        }
 
        return dev;
@@ -929,17 +937,10 @@ int input_register_device(struct input_dev *dev)
        static atomic_t input_no = ATOMIC_INIT(0);
        struct input_handle *handle;
        struct input_handler *handler;
-       struct input_device_id *id;
+       const struct input_device_id *id;
        const char *path;
        int error;
 
-       if (!dev->dynalloc) {
-               printk(KERN_WARNING "input: device %s is statically allocated, will not register\n"
-                       "Please convert to input_allocate_device() or contact dtor_core@ameritech.net\n",
-                       dev->name ? dev->name : "<Unknown>");
-               return -EINVAL;
-       }
-
        set_bit(EV_SYN, dev->evbit);
 
        /*
@@ -955,10 +956,8 @@ int input_register_device(struct input_dev *dev)
                dev->rep[REP_PERIOD] = 33;
        }
 
-       INIT_LIST_HEAD(&dev->h_list);
        list_add_tail(&dev->node, &input_dev_list);
 
-       dev->cdev.class = &input_class;
        snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
                 "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
 
@@ -978,8 +977,6 @@ int input_register_device(struct input_dev *dev)
        if (error)
                goto fail3;
 
-       __module_get(THIS_MODULE);
-
        path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
        printk(KERN_INFO "input: %s as %s\n",
                dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
@@ -1008,9 +1005,12 @@ EXPORT_SYMBOL(input_register_device);
 void input_unregister_device(struct input_dev *dev)
 {
        struct list_head *node, *next;
+       int code;
 
-       if (!dev)
-               return;
+       for (code = 0; code <= KEY_MAX; code++)
+               if (test_bit(code, dev->key))
+                       input_report_key(dev, code, 0);
+       input_sync(dev);
 
        del_timer_sync(&dev->timer);
 
@@ -1037,19 +1037,20 @@ void input_unregister_device(struct input_dev *dev)
 }
 EXPORT_SYMBOL(input_unregister_device);
 
-void input_register_handler(struct input_handler *handler)
+int input_register_handler(struct input_handler *handler)
 {
        struct input_dev *dev;
        struct input_handle *handle;
-       struct input_device_id *id;
-
-       if (!handler)
-               return;
+       const struct input_device_id *id;
 
        INIT_LIST_HEAD(&handler->h_list);
 
-       if (handler->fops != NULL)
+       if (handler->fops != NULL) {
+               if (input_table[handler->minor >> 5])
+                       return -EBUSY;
+
                input_table[handler->minor >> 5] = handler;
+       }
 
        list_add_tail(&handler->node, &input_handler_list);
 
@@ -1063,6 +1064,7 @@ void input_register_handler(struct input_handler *handler)
                                }
 
        input_wakeup_procfs_readers();
+       return 0;
 }
 EXPORT_SYMBOL(input_register_handler);
 
index d67157513bf7dc286024e182400b545bf895d0c6..9f3529ad3fdab5b149cf7c835fc32e413e6c5109 100644 (file)
@@ -451,7 +451,7 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
        }
 }
 
-static struct file_operations joydev_fops = {
+static const struct file_operations joydev_fops = {
        .owner =        THIS_MODULE,
        .read =         joydev_read,
        .write =        joydev_write,
@@ -465,7 +465,8 @@ static struct file_operations joydev_fops = {
        .fasync =       joydev_fasync,
 };
 
-static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev,
+                                          const struct input_device_id *id)
 {
        struct joydev *joydev;
        struct class_device *cdev;
@@ -562,7 +563,7 @@ static void joydev_disconnect(struct input_handle *handle)
                joydev_free(joydev);
 }
 
-static struct input_device_id joydev_blacklist[] = {
+static const struct input_device_id joydev_blacklist[] = {
        {
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
                .evbit = { BIT(EV_KEY) },
@@ -571,7 +572,7 @@ static struct input_device_id joydev_blacklist[] = {
        { }     /* Terminating entry */
 };
 
-static struct input_device_id joydev_ids[] = {
+static const struct input_device_id joydev_ids[] = {
        {
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
                .evbit = { BIT(EV_ABS) },
@@ -605,8 +606,7 @@ static struct input_handler joydev_handler = {
 
 static int __init joydev_init(void)
 {
-       input_register_handler(&joydev_handler);
-       return 0;
+       return input_register_handler(&joydev_handler);
 }
 
 static void __exit joydev_exit(void)
index 50c90765aee15274eefc8b8fd05367af14e00462..8fb0c19cc60e27b459f084e59075aea46735ca02 100644 (file)
@@ -165,19 +165,19 @@ static int make_condition_modifier(struct iforce* iforce,
        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 */
+       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;
+       center = (500 * center) >> 15;
        data[4] = LO(center);
        data[5] = HI(center);
 
-       db = (1000*db)>>16;
+       db = (1000 * db) >> 16;
        data[6] = LO(db);
        data[7] = HI(db);
 
-       data[8] = (100*rsat)>>16;
-       data[9] = (100*lsat)>>16;
+       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);
@@ -188,6 +188,7 @@ static int make_condition_modifier(struct iforce* iforce,
 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;
@@ -198,19 +199,17 @@ static unsigned char find_button(struct iforce *iforce, signed short button)
  * 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)
+static int need_condition_modifier(struct ff_effect *old, struct ff_effect *new)
 {
-       int id = new->id;
-       struct ff_effect* old = &iforce->core_effects[id].effect;
-       int ret=0;
+       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;
+               return 0;
        }
 
-       for(i=0; i<2; i++) {
+       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
@@ -225,35 +224,29 @@ static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new)
  * 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)
+static int need_magnitude_modifier(struct ff_effect *old, 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 0;
        }
 
-       return (old->u.constant.level != effect->u.constant.level);
+       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)
+static int need_envelope_modifier(struct ff_effect *old, 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;
+                       return 1;
                break;
 
        case FF_PERIODIC:
@@ -261,30 +254,26 @@ static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effec
                || 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;
+                       return 1;
                break;
 
        default:
                printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
        }
 
-       return FALSE;
+       return 0;
 }
 
 /*
  * 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)
+static int need_period_modifier(struct ff_effect *old, 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;
+               printk(KERN_WARNING "iforce.c: bad effect type in need_period_modifier\n");
+               return 0;
        }
-
        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
@@ -295,19 +284,16 @@ static int need_period_modifier(struct iforce* iforce, struct ff_effect* new)
  * 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)
+static int need_core(struct ff_effect *old, 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 1;
 
-       return FALSE;
+       return 0;
 }
 /*
  * Send the part common to all effects to the device
@@ -360,7 +346,7 @@ static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
  * 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)
+int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
 {
        u8 wave_code;
        int core_id = effect->id;
@@ -371,23 +357,25 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int
        int param2_err = 1;
        int core_err = 0;
 
-       if (!is_update || need_period_modifier(iforce, effect)) {
+       if (!old || need_period_modifier(old, effect)) {
                param1_err = make_period_modifier(iforce, mod1_chunk,
-                       is_update,
+                       old != NULL,
                        effect->u.periodic.magnitude, effect->u.periodic.offset,
                        effect->u.periodic.period, effect->u.periodic.phase);
-               if (param1_err) return param1_err;
+               if (param1_err)
+                       return param1_err;
                set_bit(FF_MOD1_IS_USED, core_effect->flags);
        }
 
-       if (!is_update || need_envelope_modifier(iforce, effect)) {
+       if (!old || need_envelope_modifier(old, effect)) {
                param2_err = make_envelope_modifier(iforce, mod2_chunk,
-                       is_update,
+                       old !=NULL,
                        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;
+               if (param2_err)
+                       return param2_err;
                set_bit(FF_MOD2_IS_USED, core_effect->flags);
        }
 
@@ -400,7 +388,7 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int
                default:                wave_code = 0x20; break;
        }
 
-       if (!is_update || need_core(iforce, effect)) {
+       if (!old || need_core(old, effect)) {
                core_err = make_core(iforce, effect->id,
                        mod1_chunk->start,
                        mod2_chunk->start,
@@ -429,7 +417,7 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int
  *  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 iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
 {
        int core_id = effect->id;
        struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
@@ -439,26 +427,28 @@ int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int
        int param2_err = 1;
        int core_err = 0;
 
-       if (!is_update || need_magnitude_modifier(iforce, effect)) {
+       if (!old || need_magnitude_modifier(old, effect)) {
                param1_err = make_magnitude_modifier(iforce, mod1_chunk,
-                       is_update,
+                       old != NULL,
                        effect->u.constant.level);
-               if (param1_err) return param1_err;
+               if (param1_err)
+                       return param1_err;
                set_bit(FF_MOD1_IS_USED, core_effect->flags);
        }
 
-       if (!is_update || need_envelope_modifier(iforce, effect)) {
+       if (!old || need_envelope_modifier(old, effect)) {
                param2_err = make_envelope_modifier(iforce, mod2_chunk,
-                       is_update,
+                       old != NULL,
                        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;
+               if (param2_err)
+                       return param2_err;
                set_bit(FF_MOD2_IS_USED, core_effect->flags);
        }
 
-       if (!is_update || need_core(iforce, effect)) {
+       if (!old || need_core(old, effect)) {
                core_err = make_core(iforce, effect->id,
                        mod1_chunk->start,
                        mod2_chunk->start,
@@ -483,7 +473,7 @@ int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int
 /*
  * 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 iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
 {
        int core_id = effect->id;
        struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
@@ -494,37 +484,39 @@ int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int
        int core_err = 0;
 
        switch (effect->type) {
-               case FF_SPRING:         type = 0x40; break;
-               case FF_DAMPER:         type = 0x41; break;
+               case FF_SPRING: type = 0x40; break;
+               case FF_DAMPER: type = 0x41; break;
                default: return -1;
        }
 
-       if (!is_update || need_condition_modifier(iforce, effect)) {
+       if (!old || need_condition_modifier(old, effect)) {
                param_err = make_condition_modifier(iforce, mod1_chunk,
-                       is_update,
+                       old != NULL,
                        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;
+               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,
+                       old != NULL,
                        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;
+               if (param_err)
+                       return param_err;
                set_bit(FF_MOD2_IS_USED, core_effect->flags);
 
        }
 
-       if (!is_update || need_core(iforce, effect)) {
+       if (!old || need_core(old, effect)) {
                core_err = make_core(iforce, effect->id,
                        mod1_chunk->start, mod2_chunk->start,
                        type, 0xc0,
index b4914e7231f863a3073da1c7e8e320e3e93c9c87..24c684bc63375917e44a06b3eae67947a501497d 100644 (file)
@@ -83,103 +83,57 @@ static struct iforce_device iforce_device[] = {
        { 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)
+static int iforce_playback(struct input_dev *dev, int effect_id, int value)
 {
        struct iforce* 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:
+       struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
 
-                       data[0] = 0x03;
-                       data[1] = value >> 9;
-                       iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+       if (value > 0)
+               set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
+       else
+               clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
 
-                       data[0] = 0x04;
-                       data[1] = 0x01;
-                       iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+       iforce_control_playback(iforce, effect_id, value);
+       return 0;
+}
 
-                       return 0;
+static void iforce_set_gain(struct input_dev *dev, u16 gain)
+{
+       struct iforce* iforce = dev->private;
+       unsigned char data[3];
 
-               default: /* Play or stop an effect */
+       data[0] = gain >> 9;
+       iforce_send_packet(iforce, FF_CMD_GAIN, data);
+}
 
-                       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);
-                       }
+static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+       struct iforce* iforce = dev->private;
+       unsigned char data[3];
 
-                       iforce_control_playback(iforce, code, value);
-                       return 0;
-       }
+       data[0] = 0x03;
+       data[1] = magnitude >> 9;
+       iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
 
-       return -1;
+       data[0] = 0x04;
+       data[1] = 0x01;
+       iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
 }
 
 /*
  * 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)
+static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
 {
        struct iforce* iforce = dev->private;
-       int id;
+       struct iforce_core_effect *core_effect = &iforce->core_effects[effect->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;
 
+       if (__test_and_set_bit(FF_CORE_IS_USED, core_effect->flags)) {
                /* Check the effect is not already being updated */
-               if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags))
+               if (test_bit(FF_CORE_UPDATE, core_effect->flags))
                        return -EAGAIN;
-
-               is_update = TRUE;
        }
 
 /*
@@ -188,28 +142,28 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
        switch (effect->type) {
 
                case FF_PERIODIC:
-                       ret = iforce_upload_periodic(iforce, effect, is_update);
+                       ret = iforce_upload_periodic(iforce, effect, old);
                        break;
 
                case FF_CONSTANT:
-                       ret = iforce_upload_constant(iforce, effect, is_update);
+                       ret = iforce_upload_constant(iforce, effect, old);
                        break;
 
                case FF_SPRING:
                case FF_DAMPER:
-                       ret = iforce_upload_condition(iforce, effect, is_update);
+                       ret = iforce_upload_condition(iforce, effect, old);
                        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);
+               set_bit(FF_CORE_UPDATE, core_effect->flags);
        }
-       iforce->core_effects[effect->id].effect = *effect;
        return ret;
 }
 
@@ -219,20 +173,9 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
  */
 static int iforce_erase_effect(struct input_dev *dev, int effect_id)
 {
-       struct iforce* iforce = dev->private;
+       struct iforce *iforce = dev->private;
+       struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
        int err = 0;
-       struct iforce_core_effect* core_effect;
-
-       if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
-               return -EINVAL;
-
-       core_effect = &iforce->core_effects[effect_id];
-
-       /* Check who is trying to erase this effect */
-       if (core_effect->owner != current->pid) {
-               printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, core_effect->owner);
-               return -EACCES;
-       }
 
        if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
                err = release_resource(&core_effect->mod1_chunk);
@@ -240,7 +183,7 @@ static int iforce_erase_effect(struct input_dev *dev, int effect_id)
        if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
                err = release_resource(&core_effect->mod2_chunk);
 
-       /*TODO: remember to change that if more FF_MOD* bits are added */
+       /* TODO: remember to change that if more FF_MOD* bits are added */
        core_effect->flags[0] = 0;
 
        return err;
@@ -260,52 +203,31 @@ static int iforce_open(struct input_dev *dev)
 #endif
        }
 
-       /* Enable force feedback */
-       iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+       if (test_bit(EV_FF, dev->evbit)) {
+               /* Enable force feedback */
+               iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+       }
 
        return 0;
 }
 
-static int iforce_flush(struct input_dev *dev, struct file *file)
+static void iforce_release(struct input_dev *dev)
 {
        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);
+       if (test_bit(EV_FF, dev->evbit)) {
+               /* Check: no effects should be present in memory */
+               for (i = 0; i < dev->ff->max_effects; i++) {
+                       if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) {
+                               printk(KERN_WARNING "iforce_release: Device still owns effects\n");
+                               break;
                        }
                }
 
+               /* Disable force feedback playback */
+               iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
        }
-       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
@@ -342,8 +264,10 @@ void iforce_delete_device(struct iforce *iforce)
 int iforce_init_device(struct iforce *iforce)
 {
        struct input_dev *input_dev;
+       struct ff_device *ff;
        unsigned char c[] = "CEOV";
-       int i;
+       int i, error;
+       int ff_effects = 0;
 
        input_dev = input_allocate_device();
        if (!input_dev)
@@ -378,11 +302,6 @@ int iforce_init_device(struct iforce *iforce)
        input_dev->name = "Unknown I-Force device";
        input_dev->open = iforce_open;
        input_dev->close = iforce_release;
-       input_dev->flush = iforce_flush;
-       input_dev->event = iforce_input_event;
-       input_dev->upload_effect = iforce_upload_effect;
-       input_dev->erase_effect = iforce_erase_effect;
-       input_dev->ff_effects_max = 10;
 
 /*
  * On-device memory allocation.
@@ -430,15 +349,15 @@ int iforce_init_device(struct iforce *iforce)
                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];
+               ff_effects = 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;
+       if (ff_effects > IFORCE_EFFECTS_MAX) {
+               printk(KERN_WARNING "iforce: Limiting number of effects to %d (device reports %d)\n",
+                      IFORCE_EFFECTS_MAX, ff_effects);
+               ff_effects = IFORCE_EFFECTS_MAX;
        }
 
 /*
@@ -472,12 +391,10 @@ int iforce_init_device(struct iforce *iforce)
  * Set input device bitfields and ranges.
  */
 
-       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS);
+       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF_STATUS);
 
-       for (i = 0; iforce->type->btn[i] >= 0; i++) {
-               signed short t = iforce->type->btn[i];
-               set_bit(t, input_dev->keybit);
-       }
+       for (i = 0; iforce->type->btn[i] >= 0; i++)
+               set_bit(iforce->type->btn[i], input_dev->keybit);
        set_bit(BTN_DEAD, input_dev->keybit);
 
        for (i = 0; iforce->type->abs[i] >= 0; i++) {
@@ -516,9 +433,24 @@ int iforce_init_device(struct iforce *iforce)
                }
        }
 
-       for (i = 0; iforce->type->ff[i] >= 0; i++)
-               set_bit(iforce->type->ff[i], input_dev->ffbit);
+       if (ff_effects) {
 
+               for (i = 0; iforce->type->ff[i] >= 0; i++)
+                       set_bit(iforce->type->ff[i], input_dev->ffbit);
+
+               error = input_ff_create(input_dev, ff_effects);
+               if (error) {
+                       input_free_device(input_dev);
+                       return error;
+               }
+
+               ff = input_dev->ff;
+               ff->upload = iforce_upload_effect;
+               ff->erase = iforce_erase_effect;
+               ff->set_gain = iforce_set_gain;
+               ff->set_autocenter = iforce_set_autocenter;
+               ff->playback = iforce_playback;
+       }
 /*
  * Register input device.
  */
index 76cb1f88f4e83d4177b8cb8c281861aef50325df..8632d47a7fbe588eed1a10cfa7f14b59845cef58 100644 (file)
@@ -140,7 +140,10 @@ 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 (!iforce->dev->ff)
+               return 0;
+
+       for (i = 0; i < iforce->dev->ff->max_effects; ++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)) {
@@ -229,19 +232,17 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data,
                        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);
+                                       /* 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)) {
+                       } 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) {
+                               for (j = 3; j < LO(cmd); j += 2)
                                        mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
-                               }
                        }
                        break;
        }
index e9924d6f01b33991b975de0272588f6f0691ea7e..947df273984399384fd0e8aece218ac388cadb43 100644 (file)
 #define IFORCE_232     1
 #define IFORCE_USB     2
 
-#define FALSE 0
-#define TRUE 1
-
-#define FF_EFFECTS_MAX 32
+#define IFORCE_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_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
@@ -145,7 +129,7 @@ struct iforce {
                                        /* Force Feedback */
        wait_queue_head_t wait;
        struct resource device_memory;
-       struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
+       struct iforce_core_effect core_effects[IFORCE_EFFECTS_MAX];
        struct mutex mem_mutex;
 };
 
@@ -182,9 +166,9 @@ 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);
+int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *);
+int iforce_upload_constant(struct iforce *, struct ff_effect *, struct ff_effect *);
+int iforce_upload_condition(struct iforce *, struct ff_effect *, struct ff_effect *);
 
 /* Public variables */
 extern struct serio_driver iforce_serio_drv;
index a86afd0a5ef13c95c869613c5fd4e67b7c414825..40244d4ce0f1696e6dc3179c7ab13e260884851a 100644 (file)
@@ -652,9 +652,7 @@ static int atkbd_probe(struct atkbd *atkbd)
                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 */
+       if (!ps2_is_keyboard_id(param[0]))
                return -1;
 
        atkbd->id = (param[0] << 8) | param[1];
index d723e9ad7c41a103ce092db1b45068b98b8acd9b..9516439b7c78837ed83ede2d6559c194b4aeb50e 100644 (file)
@@ -20,6 +20,9 @@
  * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
  *
  * Changes/Revisions:
+ *     0.3     09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
+ *             - updated ff support for the changes in kernel interface
+ *             - added MODULE_VERSION
  *     0.2     16/10/2004 (Micah Dowty <micah@navi.cx>)
  *             - added force feedback support
  *              - added UI_SET_PHYS
@@ -107,18 +110,31 @@ static int uinput_request_submit(struct input_dev *dev, struct uinput_request *r
        return request->retval;
 }
 
-static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
+static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
+{
+       uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
+}
+
+static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+       uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
+}
+
+static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
+{
+       return uinput_dev_event(dev, EV_FF, effect_id, value);
+}
+
+static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
 {
        struct uinput_request request;
        int retval;
 
-       if (!test_bit(EV_FF, dev->evbit))
-               return -ENOSYS;
-
        request.id = -1;
        init_completion(&request.done);
        request.code = UI_FF_UPLOAD;
-       request.u.effect = effect;
+       request.u.upload.effect = effect;
+       request.u.upload.old = old;
 
        retval = uinput_request_reserve_slot(dev->private, &request);
        if (!retval)
@@ -168,6 +184,7 @@ static void uinput_destroy_device(struct uinput_device *udev)
 
 static int uinput_create_device(struct uinput_device *udev)
 {
+       struct input_dev *dev = udev->dev;
        int error;
 
        if (udev->state != UIST_SETUP_COMPLETE) {
@@ -175,15 +192,29 @@ static int uinput_create_device(struct uinput_device *udev)
                return -EINVAL;
        }
 
-       error = input_register_device(udev->dev);
-       if (error) {
-               uinput_destroy_device(udev);
-               return error;
+       if (udev->ff_effects_max) {
+               error = input_ff_create(dev, udev->ff_effects_max);
+               if (error)
+                       goto fail1;
+
+               dev->ff->upload = uinput_dev_upload_effect;
+               dev->ff->erase = uinput_dev_erase_effect;
+               dev->ff->playback = uinput_dev_playback;
+               dev->ff->set_gain = uinput_dev_set_gain;
+               dev->ff->set_autocenter = uinput_dev_set_autocenter;
        }
 
+       error = input_register_device(udev->dev);
+       if (error)
+               goto fail2;
+
        udev->state = UIST_CREATED;
 
        return 0;
+
+ fail2:        input_ff_destroy(dev);
+ fail1: uinput_destroy_device(udev);
+       return error;
 }
 
 static int uinput_open(struct inode *inode, struct file *file)
@@ -243,8 +274,6 @@ static int uinput_allocate_device(struct uinput_device *udev)
                return -ENOMEM;
 
        udev->dev->event = uinput_dev_event;
-       udev->dev->upload_effect = uinput_dev_upload_effect;
-       udev->dev->erase_effect = uinput_dev_erase_effect;
        udev->dev->private = udev;
 
        return 0;
@@ -278,6 +307,8 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
                goto exit;
        }
 
+       udev->ff_effects_max = user_dev->ff_effects_max;
+
        size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
        if (!size) {
                retval = -EINVAL;
@@ -296,7 +327,6 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
        dev->id.vendor  = user_dev->id.vendor;
        dev->id.product = user_dev->id.product;
        dev->id.version = user_dev->id.version;
-       dev->ff_effects_max = user_dev->ff_effects_max;
 
        size = sizeof(int) * (ABS_MAX + 1);
        memcpy(dev->absmax, user_dev->absmax, size);
@@ -525,12 +555,17 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                break;
                        }
                        req = uinput_request_find(udev, ff_up.request_id);
-                       if (!(req && req->code == UI_FF_UPLOAD && req->u.effect)) {
+                       if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) {
                                retval = -EINVAL;
                                break;
                        }
                        ff_up.retval = 0;
-                       memcpy(&ff_up.effect, req->u.effect, sizeof(struct ff_effect));
+                       memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect));
+                       if (req->u.upload.old)
+                               memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect));
+                       else
+                               memset(&ff_up.old, 0, sizeof(struct ff_effect));
+
                        if (copy_to_user(p, &ff_up, sizeof(ff_up))) {
                                retval = -EFAULT;
                                break;
@@ -561,12 +596,11 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                break;
                        }
                        req = uinput_request_find(udev, ff_up.request_id);
-                       if (!(req && req->code == UI_FF_UPLOAD && req->u.effect)) {
+                       if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) {
                                retval = -EINVAL;
                                break;
                        }
                        req->retval = ff_up.retval;
-                       memcpy(req->u.effect, &ff_up.effect, sizeof(struct ff_effect));
                        uinput_request_done(udev, req);
                        break;
 
@@ -622,6 +656,7 @@ static void __exit uinput_exit(void)
 MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
 MODULE_DESCRIPTION("User level driver support for input subsystem");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3");
 
 module_init(uinput_init);
 module_exit(uinput_exit);
index 070d75330afd335de4dcf1691477aeb1bc8593e9..450b68a619fd4ab375db67d0b85567a1ef5455de 100644 (file)
@@ -36,7 +36,7 @@
 #define ALPS_PASS      0x20
 #define ALPS_FW_BK_2   0x40
 
-static struct alps_model_info alps_model_data[] = {
+static const struct alps_model_info alps_model_data[] = {
        { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO },            /* UMAX-530T */
        { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
        { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
@@ -209,10 +209,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs *
        return PSMOUSE_GOOD_DATA;
 }
 
-static struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
+static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
 {
        struct ps2dev *ps2dev = &psmouse->ps2dev;
-       unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
+       static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
        unsigned char param[4];
        int i;
 
@@ -504,7 +504,7 @@ init_fail:
 int alps_detect(struct psmouse *psmouse, int set_properties)
 {
        int version;
-       struct alps_model_info *model;
+       const struct alps_model_info *model;
 
        if (!(model = alps_get_model(psmouse, &version)))
                return -1;
index e428f8d5d12ece57db9e20818d11088de71e1a12..69db7325a4944a79dc2aef6a925cdb329a262a24 100644 (file)
@@ -25,7 +25,7 @@ struct alps_data {
        struct input_dev *dev2;         /* Relative device */
        char name[32];                  /* Name */
        char phys[32];                  /* Phys */
-       struct alps_model_info *i;      /* Info */
+       const struct alps_model_info *i;/* Info */
        int prev_fin;                   /* Finger bit from previous packet */
 };
 
index c14395ba798005d31a14b7c3c0e135548ed4c03d..5e9d25067513850920d4589c029f4056f4fd3625 100644 (file)
@@ -115,13 +115,15 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
 
 static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
 {
-       unsigned char params[] = { 0, 1, 2, 2, 3 };
+       static const unsigned char params[] = { 0, 1, 2, 2, 3 };
+       unsigned char p;
 
        if (resolution == 0 || resolution > 400)
                resolution = 400;
 
-       ps2_command(&psmouse->ps2dev, &params[resolution / 100], PSMOUSE_CMD_SETRES);
-       psmouse->resolution = 50 << params[resolution / 100];
+       p = params[resolution / 100];
+       ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
+       psmouse->resolution = 50 << p;
 }
 
 static void lifebook_disconnect(struct psmouse *psmouse)
index 54b696cfe1e33be4bf94b599ab8f5cc71e4562c7..7972eecbcfe4184863bdb84e0a3dda6eff31f854 100644 (file)
@@ -30,9 +30,9 @@
 #define PS2PP_NAV_BTN          0x20
 
 struct ps2pp_info {
-       const int model;
-       unsigned const int kind;
-       unsigned const int features;
+       u8 model;
+       u8 kind;
+       u16 features;
 };
 
 /*
@@ -199,9 +199,9 @@ static void ps2pp_disconnect(struct psmouse *psmouse)
        device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr);
 }
 
-static struct ps2pp_info *get_model_info(unsigned char model)
+static const struct ps2pp_info *get_model_info(unsigned char model)
 {
-       static struct ps2pp_info ps2pp_list[] = {
+       static const struct ps2pp_info ps2pp_list[] = {
                { 12,   0,                      PS2PP_SIDE_BTN},
                { 13,   0,                      0 },
                { 15,   PS2PP_KIND_MX,                                  /* MX1000 */
@@ -215,6 +215,7 @@ static struct ps2pp_info *get_model_info(unsigned char model)
                { 51,   0,                      0 },
                { 52,   PS2PP_KIND_WHEEL,       PS2PP_SIDE_BTN | PS2PP_WHEEL },
                { 53,   PS2PP_KIND_WHEEL,       PS2PP_WHEEL },
+               { 56,   PS2PP_KIND_WHEEL,       PS2PP_SIDE_BTN | PS2PP_WHEEL }, /* Cordless MouseMan Wheel */
                { 61,   PS2PP_KIND_MX,                                  /* MX700 */
                                PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
                                PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
@@ -244,12 +245,11 @@ static struct ps2pp_info *get_model_info(unsigned char model)
                                PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
                { 114,  PS2PP_KIND_MX,                                  /* MX310 */
                                PS2PP_WHEEL | PS2PP_SIDE_BTN |
-                               PS2PP_TASK_BTN | PS2PP_EXTRA_BTN },
-               { }
+                               PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }
        };
        int i;
 
-       for (i = 0; ps2pp_list[i].model; i++)
+       for (i = 0; i < ARRAY_SIZE(ps2pp_list); i++)
                if (model == ps2pp_list[i].model)
                        return &ps2pp_list[i];
 
@@ -261,7 +261,8 @@ static struct ps2pp_info *get_model_info(unsigned char model)
  * Set up input device's properties based on the detected mouse model.
  */
 
-static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_info *model_info,
+static void ps2pp_set_model_properties(struct psmouse *psmouse,
+                                      const struct ps2pp_info *model_info,
                                       int using_ps2pp)
 {
        struct input_dev *input_dev = psmouse->dev;
@@ -327,7 +328,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        unsigned char param[4];
        unsigned char model, buttons;
-       struct ps2pp_info *model_info;
+       const struct ps2pp_info *model_info;
        int use_ps2pp = 0;
 
        param[0] = 0;
@@ -349,7 +350,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
 /*
  * Do Logitech PS2++ / PS2T++ magic init.
  */
-               if (model == 97) { /* Touch Pad 3 */
+               if (model_info->kind == PS2PP_KIND_TP3) { /* Touch Pad 3 */
 
                        /* Unprotect RAM */
                        param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
index 343afa38f4c2d2b1c38ae809097ea343d44a670a..9fb7eb6b0f71f3bb9f5d7972e99627146a463fa7 100644 (file)
@@ -112,8 +112,8 @@ static struct workqueue_struct *kpsmoused_wq;
 
 struct psmouse_protocol {
        enum psmouse_type type;
-       char *name;
-       char *alias;
+       const char *name;
+       const char *alias;
        int maxproto;
        int (*detect)(struct psmouse *, int);
        int (*init)(struct psmouse *);
@@ -507,15 +507,17 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties)
 {
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        unsigned char param[2];
-       unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20, 0 };
+       static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
        int i;
 
        param[0] = 10;
        ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
        param[0] = 0;
        ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
-       for (i = 0; seq[i]; i++)
-               ps2_command(ps2dev, seq + i, PSMOUSE_CMD_SETRATE);
+       for (i = 0; i < ARRAY_SIZE(seq); i++) {
+               param[0] = seq[i];
+               ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+       }
        ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
 
        if (param[0] != 2)
@@ -652,7 +654,7 @@ static int psmouse_extensions(struct psmouse *psmouse,
        return PSMOUSE_PS2;
 }
 
-static struct psmouse_protocol psmouse_protocols[] = {
+static const struct psmouse_protocol psmouse_protocols[] = {
        {
                .type           = PSMOUSE_PS2,
                .name           = "PS/2",
@@ -726,7 +728,7 @@ static struct psmouse_protocol psmouse_protocols[] = {
        },
 };
 
-static struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
 {
        int i;
 
@@ -738,9 +740,9 @@ static struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
        return &psmouse_protocols[0];
 }
 
-static struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len)
+static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len)
 {
-       struct psmouse_protocol *p;
+       const struct psmouse_protocol *p;
        int i;
 
        for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) {
@@ -795,13 +797,15 @@ static int psmouse_probe(struct psmouse *psmouse)
 
 void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
 {
-       unsigned char params[] = { 0, 1, 2, 2, 3 };
+       static const unsigned char params[] = { 0, 1, 2, 2, 3 };
+       unsigned char p;
 
        if (resolution == 0 || resolution > 200)
                resolution = 200;
 
-       ps2_command(&psmouse->ps2dev, &params[resolution / 50], PSMOUSE_CMD_SETRES);
-       psmouse->resolution = 25 << params[resolution / 50];
+       p = params[resolution / 50];
+       ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
+       psmouse->resolution = 25 << p;
 }
 
 /*
@@ -810,12 +814,14 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
 
 static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
 {
-       unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
+       static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
+       unsigned char r;
        int i = 0;
 
        while (rates[i] > rate) i++;
-       ps2_command(&psmouse->ps2dev, &rates[i], PSMOUSE_CMD_SETRATE);
-       psmouse->rate = rates[i];
+       r = rates[i];
+       ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
+       psmouse->rate = r;
 }
 
 /*
@@ -1031,7 +1037,7 @@ static void psmouse_disconnect(struct serio *serio)
        mutex_unlock(&psmouse_mutex);
 }
 
-static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_protocol *proto)
+static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto)
 {
        struct input_dev *input_dev = psmouse->dev;
 
@@ -1362,7 +1368,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
        struct serio *serio = psmouse->ps2dev.serio;
        struct psmouse *parent = NULL;
        struct input_dev *new_dev;
-       struct psmouse_protocol *proto;
+       const struct psmouse_protocol *proto;
        int retry = 0;
 
        if (!(proto = psmouse_protocol_by_name(buf, count)))
@@ -1459,7 +1465,7 @@ static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data,
 
 static int psmouse_set_maxproto(const char *val, struct kernel_param *kp)
 {
-       struct psmouse_protocol *proto;
+       const struct psmouse_protocol *proto;
 
        if (!val)
                return -EINVAL;
index 0023501a5b634fd086837d3e1e9dc49f116414d4..680b323538847cbc53dfa7a99f9d5ae9b97d97ce 100644 (file)
@@ -42,7 +42,7 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
-static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
+static const char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
                                        "Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse",
                                        "Logitech MZ++ Mouse"};
 
index ad5d0a85e960157b335915e693a87872e4a9bf96..392108c436baca504abd345b4013ae90799e41e5 100644 (file)
@@ -430,11 +430,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
 
 static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
 {
-       static unsigned char newabs_mask[]      = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
-       static unsigned char newabs_rel_mask[]  = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
-       static unsigned char newabs_rslt[]      = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
-       static unsigned char oldabs_mask[]      = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
-       static unsigned char oldabs_rslt[]      = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
+       static const unsigned char newabs_mask[]        = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
+       static const unsigned char newabs_rel_mask[]    = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
+       static const unsigned char newabs_rslt[]        = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
+       static const unsigned char oldabs_mask[]        = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
+       static const unsigned char oldabs_rslt[]        = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
 
        if (idx < 0 || idx > 4)
                return 0;
index 1f851acab30db2f692ab74886256701409432110..a22a74a2a3dcc6e781b79609360830b95208c026 100644 (file)
@@ -614,7 +614,7 @@ static unsigned int mousedev_poll(struct file *file, poll_table *wait)
                (list->mousedev->exist ? 0 : (POLLHUP | POLLERR));
 }
 
-static struct file_operations mousedev_fops = {
+static const struct file_operations mousedev_fops = {
        .owner =        THIS_MODULE,
        .read =         mousedev_read,
        .write =        mousedev_write,
@@ -624,7 +624,8 @@ static struct file_operations mousedev_fops = {
        .fasync =       mousedev_fasync,
 };
 
-static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev,
+                                            const struct input_device_id *id)
 {
        struct mousedev *mousedev;
        struct class_device *cdev;
@@ -688,7 +689,7 @@ static void mousedev_disconnect(struct input_handle *handle)
        }
 }
 
-static struct input_device_id mousedev_ids[] = {
+static const struct input_device_id mousedev_ids[] = {
        {
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
                .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
@@ -737,7 +738,12 @@ static int psaux_registered;
 
 static int __init mousedev_init(void)
 {
-       input_register_handler(&mousedev_handler);
+       struct class_device *cdev;
+       int error;
+
+       error = input_register_handler(&mousedev_handler);
+       if (error)
+               return error;
 
        memset(&mousedev_mix, 0, sizeof(struct mousedev));
        INIT_LIST_HEAD(&mousedev_mix.list);
@@ -746,12 +752,20 @@ static int __init mousedev_init(void)
        mousedev_mix.exist = 1;
        mousedev_mix.minor = MOUSEDEV_MIX;
 
-       class_device_create(&input_class, NULL,
+       cdev = class_device_create(&input_class, NULL,
                        MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice");
+       if (IS_ERR(cdev)) {
+               input_unregister_handler(&mousedev_handler);
+               return PTR_ERR(cdev);
+       }
 
 #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
-       if (!(psaux_registered = !misc_register(&psaux_mouse)))
-               printk(KERN_WARNING "mice: could not misc_register the device\n");
+       error = misc_register(&psaux_mouse);
+       if (error)
+               printk(KERN_WARNING "mice: could not register psaux device, "
+                       "error: %d\n", error);
+       else
+               psaux_registered = 1;
 #endif
 
        printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
index 51a519e24b6d07bd146206d47cd1293719dedb48..ee82464a2fa73c5bfd2b203fdfe68a8cd6ccfb61 100644 (file)
@@ -98,7 +98,7 @@ static void power_event(struct input_handle *handle, unsigned int type,
 
 static struct input_handle *power_connect(struct input_handler *handler,
                                          struct input_dev *dev,
-                                         struct input_device_id *id)
+                                         const struct input_device_id *id)
 {
        struct input_handle *handle;
 
@@ -120,7 +120,7 @@ static void power_disconnect(struct input_handle *handle)
        kfree(handle);
 }
 
-static struct input_device_id power_ids[] = {
+static const struct input_device_id power_ids[] = {
        {
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
                .evbit = { BIT(EV_KEY) },
@@ -150,8 +150,7 @@ static struct input_handler power_handler = {
 
 static int __init power_init(void)
 {
-       input_register_handler(&power_handler);
-       return 0;
+       return input_register_handler(&power_handler);
 }
 
 static void __exit power_exit(void)
index f606e96bc2f4b5e4f5e04f22f0440c94c3453607..8738edda661001236e0db92c0607ec98fa0496b7 100644 (file)
@@ -159,6 +159,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"),
                },
        },
+       {
+               .ident = "Toshiba Equium A110",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
+               },
+       },
        {
                .ident = "Alienware Sentia",
                .matches = {
@@ -180,6 +187,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"),
                },
        },
+       {
+               .ident = "Amoi M636/A737",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"),
+               },
+       },
        { }
 };
 
index 06a3f25657dd29062455027e076cbab0c99165b5..1bb0c76a9259b182c2153a86472965712b134c6d 100644 (file)
@@ -90,46 +90,24 @@ static DEFINE_SPINLOCK(i8042_lock);
 struct i8042_port {
        struct serio *serio;
        int irq;
-       unsigned char disable;
-       unsigned char irqen;
        unsigned char exists;
        signed char mux;
-       char name[8];
 };
 
 #define I8042_KBD_PORT_NO      0
 #define I8042_AUX_PORT_NO      1
 #define I8042_MUX_PORT_NO      2
 #define I8042_NUM_PORTS                (I8042_NUM_MUX_PORTS + 2)
-static struct i8042_port i8042_ports[I8042_NUM_PORTS] = {
-       {
-               .disable        = I8042_CTR_KBDDIS,
-               .irqen          = I8042_CTR_KBDINT,
-               .mux            = -1,
-               .name           = "KBD",
-       },
-       {
-               .disable        = I8042_CTR_AUXDIS,
-               .irqen          = I8042_CTR_AUXINT,
-               .mux            = -1,
-               .name           = "AUX",
-       }
-};
+
+static struct i8042_port i8042_ports[I8042_NUM_PORTS];
 
 static unsigned char i8042_initial_ctr;
 static unsigned char i8042_ctr;
-static unsigned char i8042_mux_open;
 static unsigned char i8042_mux_present;
-static struct timer_list i8042_timer;
+static unsigned char i8042_kbd_irq_registered;
+static unsigned char i8042_aux_irq_registered;
 static struct platform_device *i8042_platform_device;
 
-
-/*
- * Shared IRQ's require a device pointer, but this driver doesn't support
- * multiple devices
- */
-#define i8042_request_irq_cookie (&i8042_timer)
-
 static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
 /*
@@ -141,6 +119,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static int i8042_wait_read(void)
 {
        int i = 0;
+
        while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
                udelay(50);
                i++;
@@ -151,6 +130,7 @@ static int i8042_wait_read(void)
 static int i8042_wait_write(void)
 {
        int i = 0;
+
        while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
                udelay(50);
                i++;
@@ -192,48 +172,57 @@ static int i8042_flush(void)
  * encoded in bits 8-11 of the command number.
  */
 
-static int i8042_command(unsigned char *param, int command)
+static int __i8042_command(unsigned char *param, int command)
 {
-       unsigned long flags;
-       int i, retval, auxerr = 0;
+       int i, error;
 
        if (i8042_noloop && command == I8042_CMD_AUX_LOOP)
                return -1;
 
-       spin_lock_irqsave(&i8042_lock, flags);
-
-       if ((retval = i8042_wait_write()))
-               goto out;
+       error = i8042_wait_write();
+       if (error)
+               return error;
 
        dbg("%02x -> i8042 (command)", command & 0xff);
        i8042_write_command(command & 0xff);
 
        for (i = 0; i < ((command >> 12) & 0xf); i++) {
-               if ((retval = i8042_wait_write()))
-                       goto out;
+               error = i8042_wait_write();
+               if (error)
+                       return error;
                dbg("%02x -> i8042 (parameter)", param[i]);
                i8042_write_data(param[i]);
        }
 
        for (i = 0; i < ((command >> 8) & 0xf); i++) {
-               if ((retval = i8042_wait_read()))
-                       goto out;
+               error = i8042_wait_read();
+               if (error) {
+                       dbg("     -- i8042 (timeout)");
+                       return error;
+               }
 
                if (command == I8042_CMD_AUX_LOOP &&
                    !(i8042_read_status() & I8042_STR_AUXDATA)) {
-                       retval = auxerr = -1;
-                       goto out;
+                       dbg("     -- i8042 (auxerr)");
+                       return -1;
                }
 
                param[i] = i8042_read_data();
                dbg("%02x <- i8042 (return)", param[i]);
        }
 
-       if (retval)
-               dbg("     -- i8042 (%s)", auxerr ? "auxerr" : "timeout");
+       return 0;
+}
 
- out:
+static int i8042_command(unsigned char *param, int command)
+{
+       unsigned long flags;
+       int retval;
+
+       spin_lock_irqsave(&i8042_lock, flags);
+       retval = __i8042_command(param, command);
        spin_unlock_irqrestore(&i8042_lock, flags);
+
        return retval;
 }
 
@@ -248,7 +237,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
 
        spin_lock_irqsave(&i8042_lock, flags);
 
-       if(!(retval = i8042_wait_write())) {
+       if (!(retval = i8042_wait_write())) {
                dbg("%02x -> i8042 (kbd-data)", c);
                i8042_write_data(c);
        }
@@ -286,100 +275,6 @@ static int i8042_aux_write(struct serio *serio, unsigned char c)
        return retval;
 }
 
-/*
- * i8042_activate_port() enables port on a chip.
- */
-
-static int i8042_activate_port(struct i8042_port *port)
-{
-       if (!port->serio)
-               return -1;
-
-       i8042_flush();
-
-       /*
-        * Enable port again here because it is disabled if we are
-        * resuming (normally it is enabled already).
-        */
-       i8042_ctr &= ~port->disable;
-
-       i8042_ctr |= port->irqen;
-
-       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
-               i8042_ctr &= ~port->irqen;
-               return -1;
-       }
-
-       return 0;
-}
-
-
-/*
- * i8042_open() is called when a port is open by the higher layer.
- * It allocates the interrupt and calls i8042_enable_port.
- */
-
-static int i8042_open(struct serio *serio)
-{
-       struct i8042_port *port = serio->port_data;
-
-       if (port->mux != -1)
-               if (i8042_mux_open++)
-                       return 0;
-
-       if (request_irq(port->irq, i8042_interrupt,
-                       IRQF_SHARED, "i8042", i8042_request_irq_cookie)) {
-               printk(KERN_ERR "i8042.c: Can't get irq %d for %s, unregistering the port.\n", port->irq, port->name);
-               goto irq_fail;
-       }
-
-       if (i8042_activate_port(port)) {
-               printk(KERN_ERR "i8042.c: Can't activate %s, unregistering the port\n", port->name);
-               goto activate_fail;
-       }
-
-       i8042_interrupt(0, NULL, NULL);
-
-       return 0;
-
- activate_fail:
-       free_irq(port->irq, i8042_request_irq_cookie);
-
- irq_fail:
-       serio_unregister_port_delayed(serio);
-
-       return -1;
-}
-
-/*
- * i8042_close() frees the interrupt, so that it can possibly be used
- * by another driver. We never know - if the user doesn't have a mouse,
- * the BIOS could have used the AUX interrupt for PCI.
- */
-
-static void i8042_close(struct serio *serio)
-{
-       struct i8042_port *port = serio->port_data;
-
-       if (port->mux != -1)
-               if (--i8042_mux_open)
-                       return;
-
-       i8042_ctr &= ~port->irqen;
-
-       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
-               printk(KERN_WARNING "i8042.c: Can't write CTR while closing %s.\n", port->name);
-/*
- * We still want to continue and free IRQ so if more data keeps coming in
- * kernel will just ignore the irq.
- */
-       }
-
-       free_irq(port->irq, i8042_request_irq_cookie);
-
-       i8042_flush();
-}
-
 /*
  * i8042_start() is called by serio core when port is about to finish
  * registering. It will mark port as existing so i8042_interrupt can
@@ -423,8 +318,6 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        unsigned int port_no;
        int ret;
 
-       mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
-
        spin_lock_irqsave(&i8042_lock, flags);
        str = i8042_read_status();
        if (unlikely(~str & I8042_STR_OBF)) {
@@ -480,8 +373,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
        port = &i8042_ports[port_no];
 
-       dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",
-           data, port->name, irq,
+       dbg("%02x <- i8042 (interrupt, %d, %d%s%s)",
+           data, port_no, irq,
            dfl & SERIO_PARITY ? ", bad parity" : "",
            dfl & SERIO_TIMEOUT ? ", timeout" : "");
 
@@ -493,6 +386,58 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        return IRQ_RETVAL(ret);
 }
 
+/*
+ * i8042_enable_kbd_port enables keybaord port on chip
+ */
+
+static int i8042_enable_kbd_port(void)
+{
+       i8042_ctr &= ~I8042_CTR_KBDDIS;
+       i8042_ctr |= I8042_CTR_KBDINT;
+
+       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+               printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/*
+ * i8042_enable_aux_port enables AUX (mouse) port on chip
+ */
+
+static int i8042_enable_aux_port(void)
+{
+       i8042_ctr &= ~I8042_CTR_AUXDIS;
+       i8042_ctr |= I8042_CTR_AUXINT;
+
+       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+               printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/*
+ * i8042_enable_mux_ports enables 4 individual AUX ports after
+ * the controller has been switched into Multiplexed mode
+ */
+
+static int i8042_enable_mux_ports(void)
+{
+       unsigned char param;
+       int i;
+
+       for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
+               i8042_command(&param, I8042_CMD_MUX_PFX + i);
+               i8042_command(&param, I8042_CMD_AUX_ENABLE);
+       }
+
+       return i8042_enable_aux_port();
+}
+
 /*
  * i8042_set_mux_mode checks whether the controller has an active
  * multiplexor and puts the chip into Multiplexed (1) or Legacy (0) mode.
@@ -510,8 +455,7 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version)
 
 /*
  * Internal loopback test - send three bytes, they should come back from the
- * mouse interface, the last should be version. Note that we negate mouseport
- * command responses for the i8042_check_aux() routine.
+ * mouse interface, the last should be version.
  */
 
        param = 0xf0;
@@ -530,67 +474,67 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version)
        return 0;
 }
 
-
 /*
- * i8042_enable_mux_ports enables 4 individual AUX ports after
- * the controller has been switched into Multiplexed mode
+ * i8042_check_mux() checks whether the controller supports the PS/2 Active
+ * Multiplexing specification by Synaptics, Phoenix, Insyde and
+ * LCS/Telegraphics.
  */
 
-static int i8042_enable_mux_ports(void)
+static int __devinit i8042_check_mux(void)
 {
-       unsigned char param;
-       int i;
+       unsigned char mux_version;
+
+       if (i8042_set_mux_mode(1, &mux_version))
+               return -1;
+
 /*
- * Disable all muxed ports by disabling AUX.
+ * Workaround for interference with USB Legacy emulation
+ * that causes a v10.12 MUX to be found.
  */
+       if (mux_version == 0xAC)
+               return -1;
+
+       printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
+               (mux_version >> 4) & 0xf, mux_version & 0xf);
 
+/*
+ * Disable all muxed ports by disabling AUX.
+ */
        i8042_ctr |= I8042_CTR_AUXDIS;
        i8042_ctr &= ~I8042_CTR_AUXINT;
 
        if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
                printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n");
-               return -1;
+               return -EIO;
        }
 
-/*
- * Enable all muxed ports.
- */
-
-       for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
-               i8042_command(&param, I8042_CMD_MUX_PFX + i);
-               i8042_command(&param, I8042_CMD_AUX_ENABLE);
-       }
+       i8042_mux_present = 1;
 
        return 0;
 }
 
-
 /*
- * i8042_check_mux() checks whether the controller supports the PS/2 Active
- * Multiplexing specification by Synaptics, Phoenix, Insyde and
- * LCS/Telegraphics.
+ * The following is used to test AUX IRQ delivery.
  */
+static struct completion i8042_aux_irq_delivered __devinitdata;
+static int i8042_irq_being_tested __devinitdata;
 
-static int __devinit i8042_check_mux(void)
+static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id, struct pt_regs *regs)
 {
-       unsigned char mux_version;
-
-       if (i8042_set_mux_mode(1, &mux_version))
-               return -1;
-
-       /* Workaround for interference with USB Legacy emulation */
-       /* that causes a v10.12 MUX to be found. */
-       if (mux_version == 0xAC)
-               return -1;
-
-       printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
-               (mux_version >> 4) & 0xf, mux_version & 0xf);
+       unsigned long flags;
+       unsigned char str, data;
 
-       if (i8042_enable_mux_ports())
-               return -1;
+       spin_lock_irqsave(&i8042_lock, flags);
+       str = i8042_read_status();
+       if (str & I8042_STR_OBF) {
+               data = i8042_read_data();
+               if (i8042_irq_being_tested &&
+                   data == 0xa5 && (str & I8042_STR_AUXDATA))
+                       complete(&i8042_aux_irq_delivered);
+       }
+       spin_unlock_irqrestore(&i8042_lock, flags);
 
-       i8042_mux_present = 1;
-       return 0;
+       return IRQ_HANDLED;
 }
 
 
@@ -601,18 +545,10 @@ static int __devinit i8042_check_mux(void)
 
 static int __devinit i8042_check_aux(void)
 {
+       int retval = -1;
+       int irq_registered = 0;
+       unsigned long flags;
        unsigned char param;
-       static int i8042_check_aux_cookie;
-
-/*
- * Check if AUX irq is available. If it isn't, then there is no point
- * in trying to detect AUX presence.
- */
-
-       if (request_irq(i8042_ports[I8042_AUX_PORT_NO].irq, i8042_interrupt,
-                       IRQF_SHARED, "i8042", &i8042_check_aux_cookie))
-                return -1;
-       free_irq(i8042_ports[I8042_AUX_PORT_NO].irq, &i8042_check_aux_cookie);
 
 /*
  * Get rid of bytes in the queue.
@@ -637,9 +573,9 @@ static int __devinit i8042_check_aux(void)
  * AUX ports, we test for this only when the LOOP command failed.
  */
 
-               if (i8042_command(&param, I8042_CMD_AUX_TEST)
-                       || (param && param != 0xfa && param != 0xff))
-                               return -1;
+               if (i8042_command(&param, I8042_CMD_AUX_TEST) ||
+                   (param && param != 0xfa && param != 0xff))
+                       return -1;
        }
 
 /*
@@ -659,54 +595,80 @@ static int __devinit i8042_check_aux(void)
                return -1;
 
 /*
- * Disable the interface.
+ * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and
+ * used it for a PCI card or somethig else.
  */
 
-       i8042_ctr |= I8042_CTR_AUXDIS;
-       i8042_ctr &= ~I8042_CTR_AUXINT;
+       if (i8042_noloop) {
+/*
+ * Without LOOP command we can't test AUX IRQ delivery. Assume the port
+ * is working and hope we are right.
+ */
+               retval = 0;
+               goto out;
+       }
 
-       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
-               return -1;
+       if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED,
+                       "i8042", i8042_platform_device))
+               goto out;
 
-       return 0;
-}
+       irq_registered = 1;
+
+       if (i8042_enable_aux_port())
+               goto out;
+
+       spin_lock_irqsave(&i8042_lock, flags);
 
+       init_completion(&i8042_aux_irq_delivered);
+       i8042_irq_being_tested = 1;
+
+       param = 0xa5;
+       retval = __i8042_command(&param, I8042_CMD_AUX_LOOP & 0xf0ff);
+
+       spin_unlock_irqrestore(&i8042_lock, flags);
+
+       if (retval)
+               goto out;
 
+       if (wait_for_completion_timeout(&i8042_aux_irq_delivered,
+                                       msecs_to_jiffies(250)) == 0) {
 /*
- * i8042_port_register() marks the device as existing,
- * registers it, and reports to the user.
+ * AUX IRQ was never delivered so we need to flush the controller to
+ * get rid of the byte we put there; otherwise keyboard may not work.
  */
+               i8042_flush();
+               retval = -1;
+       }
 
-static int __devinit i8042_port_register(struct i8042_port *port)
-{
-       i8042_ctr &= ~port->disable;
+ out:
 
-       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
-               printk(KERN_WARNING "i8042.c: Can't write CTR while registering.\n");
-               kfree(port->serio);
-               port->serio = NULL;
-               i8042_ctr |= port->disable;
-               return -EIO;
-       }
+/*
+ * Disable the interface.
+ */
 
-       printk(KERN_INFO "serio: i8042 %s port at %#lx,%#lx irq %d\n",
-              port->name,
-              (unsigned long) I8042_DATA_REG,
-              (unsigned long) I8042_COMMAND_REG,
-              port->irq);
+       i8042_ctr |= I8042_CTR_AUXDIS;
+       i8042_ctr &= ~I8042_CTR_AUXINT;
 
-       serio_register_port(port->serio);
+       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+               retval = -1;
 
-       return 0;
-}
+       if (irq_registered)
+               free_irq(I8042_AUX_IRQ, i8042_platform_device);
 
+       return retval;
+}
 
-static void i8042_timer_func(unsigned long data)
+static int i8042_controller_check(void)
 {
-       i8042_interrupt(0, NULL, NULL);
+       if (i8042_flush() == I8042_BUFFER_SIZE) {
+               printk(KERN_ERR "i8042.c: No controller found.\n");
+               return -ENODEV;
+       }
+
+       return 0;
 }
 
-static int i8042_ctl_test(void)
+static int i8042_controller_selftest(void)
 {
        unsigned char param;
 
@@ -715,13 +677,13 @@ static int i8042_ctl_test(void)
 
        if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
                printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
-               return -1;
+               return -ENODEV;
        }
 
        if (param != I8042_RET_CTL_TEST) {
                printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
                         param, I8042_RET_CTL_TEST);
-               return -1;
+               return -EIO;
        }
 
        return 0;
@@ -737,26 +699,13 @@ static int i8042_controller_init(void)
 {
        unsigned long flags;
 
-/*
- * Test the i8042. We need to know if it thinks it's working correctly
- * before doing anything else.
- */
-
-       if (i8042_flush() == I8042_BUFFER_SIZE) {
-               printk(KERN_ERR "i8042.c: No controller found.\n");
-               return -1;
-       }
-
-       if (i8042_ctl_test())
-               return -1;
-
 /*
  * Save the CTR for restoral on unload / reboot.
  */
 
        if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
                printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n");
-               return -1;
+               return -EIO;
        }
 
        i8042_initial_ctr = i8042_ctr;
@@ -805,7 +754,7 @@ static int i8042_controller_init(void)
 
        if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
                printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
-               return -1;
+               return -EIO;
        }
 
        return 0;
@@ -813,15 +762,12 @@ static int i8042_controller_init(void)
 
 
 /*
- * Reset the controller.
+ * Reset the controller and reset CRT to the original value set by BIOS.
  */
+
 static void i8042_controller_reset(void)
 {
-/*
- * Reset the controller if requested.
- */
-
-       i8042_ctl_test();
+       i8042_flush();
 
 /*
  * Disable MUX mode if present.
@@ -831,12 +777,16 @@ static void i8042_controller_reset(void)
                i8042_set_mux_mode(0, NULL);
 
 /*
- * Restore the original control register setting.
+ * Reset the controller if requested.
  */
 
-       i8042_ctr = i8042_initial_ctr;
+       i8042_controller_selftest();
 
-       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+/*
+ * Restore the original control register setting.
+ */
+
+       if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR))
                printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
 }
 
@@ -850,14 +800,12 @@ static void i8042_controller_cleanup(void)
 {
        int i;
 
-       i8042_flush();
-
 /*
  * Reset anything that is connected to the ports.
  */
 
        for (i = 0; i < I8042_NUM_PORTS; i++)
-               if (i8042_ports[i].exists)
+               if (i8042_ports[i].serio)
                        serio_cleanup(i8042_ports[i].serio);
 
        i8042_controller_reset();
@@ -913,8 +861,7 @@ static long i8042_panic_blink(long count)
 
 static int i8042_suspend(struct platform_device *dev, pm_message_t state)
 {
-       del_timer_sync(&i8042_timer);
-       i8042_controller_reset();
+       i8042_controller_cleanup();
 
        return 0;
 }
@@ -926,33 +873,39 @@ static int i8042_suspend(struct platform_device *dev, pm_message_t state)
 
 static int i8042_resume(struct platform_device *dev)
 {
-       int i;
+       int error;
 
-       if (i8042_ctl_test())
-               return -1;
+       error = i8042_controller_check();
+       if (error)
+               return error;
 
-       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
-               printk(KERN_ERR "i8042: Can't write CTR\n");
-               return -1;
-       }
-
-       if (i8042_mux_present)
-               if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports())
-                       printk(KERN_WARNING "i8042: failed to resume active multiplexor, mouse won't work.\n");
+       error = i8042_controller_selftest();
+       if (error)
+               return error;
 
 /*
- * Activate all ports.
+ * Restore pre-resume CTR value and disable all ports
  */
 
-       for (i = 0; i < I8042_NUM_PORTS; i++)
-               i8042_activate_port(&i8042_ports[i]);
+       i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS;
+       i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT);
+       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+               printk(KERN_ERR "i8042: Can't write CTR to resume\n");
+               return -EIO;
+       }
 
-/*
- * Restart timer (for polling "stuck" data)
- */
-       mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
+       if (i8042_mux_present) {
+               if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports())
+                       printk(KERN_WARNING
+                               "i8042: failed to resume active multiplexor, "
+                               "mouse won't work.\n");
+       } else if (i8042_ports[I8042_AUX_PORT_NO].serio)
+               i8042_enable_aux_port();
 
-       panic_blink = i8042_panic_blink;
+       if (i8042_ports[I8042_KBD_PORT_NO].serio)
+               i8042_enable_kbd_port();
+
+       i8042_interrupt(0, NULL, NULL);
 
        return 0;
 }
@@ -978,24 +931,24 @@ static int __devinit i8042_create_kbd_port(void)
 
        serio->id.type          = i8042_direct ? SERIO_8042 : SERIO_8042_XL;
        serio->write            = i8042_dumbkbd ? NULL : i8042_kbd_write;
-       serio->open             = i8042_open;
-       serio->close            = i8042_close;
        serio->start            = i8042_start;
        serio->stop             = i8042_stop;
        serio->port_data        = port;
        serio->dev.parent       = &i8042_platform_device->dev;
-       strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name));
+       strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
        strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
 
        port->serio = serio;
+       port->irq = I8042_KBD_IRQ;
 
-       return i8042_port_register(port);
+       return 0;
 }
 
-static int __devinit i8042_create_aux_port(void)
+static int __devinit i8042_create_aux_port(int idx)
 {
        struct serio *serio;
-       struct i8042_port *port = &i8042_ports[I8042_AUX_PORT_NO];
+       int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx;
+       struct i8042_port *port = &i8042_ports[port_no];
 
        serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
        if (!serio)
@@ -1003,111 +956,191 @@ static int __devinit i8042_create_aux_port(void)
 
        serio->id.type          = SERIO_8042;
        serio->write            = i8042_aux_write;
-       serio->open             = i8042_open;
-       serio->close            = i8042_close;
        serio->start            = i8042_start;
        serio->stop             = i8042_stop;
        serio->port_data        = port;
        serio->dev.parent       = &i8042_platform_device->dev;
-       strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name));
-       strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+       if (idx < 0) {
+               strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name));
+               strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+       } else {
+               snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx);
+               snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1);
+       }
 
        port->serio = serio;
+       port->mux = idx;
+       port->irq = I8042_AUX_IRQ;
 
-       return i8042_port_register(port);
+       return 0;
 }
 
-static int __devinit i8042_create_mux_port(int index)
+static void __devinit i8042_free_kbd_port(void)
 {
-       struct serio *serio;
-       struct i8042_port *port = &i8042_ports[I8042_MUX_PORT_NO + index];
+       kfree(i8042_ports[I8042_KBD_PORT_NO].serio);
+       i8042_ports[I8042_KBD_PORT_NO].serio = NULL;
+}
 
-       serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
-       if (!serio)
-               return -ENOMEM;
+static void __devinit i8042_free_aux_ports(void)
+{
+       int i;
 
-       serio->id.type          = SERIO_8042;
-       serio->write            = i8042_aux_write;
-       serio->open             = i8042_open;
-       serio->close            = i8042_close;
-       serio->start            = i8042_start;
-       serio->stop             = i8042_stop;
-       serio->port_data        = port;
-       serio->dev.parent       = &i8042_platform_device->dev;
-       snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index);
-       snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1);
+       for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) {
+               kfree(i8042_ports[i].serio);
+               i8042_ports[i].serio = NULL;
+       }
+}
 
-       *port = i8042_ports[I8042_AUX_PORT_NO];
-       port->exists = 0;
-       snprintf(port->name, sizeof(port->name), "AUX%d", index);
-       port->mux = index;
-       port->serio = serio;
+static void __devinit i8042_register_ports(void)
+{
+       int i;
 
-       return i8042_port_register(port);
+       for (i = 0; i < I8042_NUM_PORTS; i++) {
+               if (i8042_ports[i].serio) {
+                       printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n",
+                               i8042_ports[i].serio->name,
+                               (unsigned long) I8042_DATA_REG,
+                               (unsigned long) I8042_COMMAND_REG,
+                               i8042_ports[i].irq);
+                       serio_register_port(i8042_ports[i].serio);
+               }
+       }
 }
 
-static int __devinit i8042_probe(struct platform_device *dev)
+static void __devinit i8042_unregister_ports(void)
 {
-       int i, have_ports = 0;
-       int err;
+       int i;
 
-       init_timer(&i8042_timer);
-       i8042_timer.function = i8042_timer_func;
+       for (i = 0; i < I8042_NUM_PORTS; i++) {
+               if (i8042_ports[i].serio) {
+                       serio_unregister_port(i8042_ports[i].serio);
+                       i8042_ports[i].serio = NULL;
+               }
+       }
+}
+
+static void i8042_free_irqs(void)
+{
+       if (i8042_aux_irq_registered)
+               free_irq(I8042_AUX_IRQ, i8042_platform_device);
+       if (i8042_kbd_irq_registered)
+               free_irq(I8042_KBD_IRQ, i8042_platform_device);
+
+       i8042_aux_irq_registered = i8042_kbd_irq_registered = 0;
+}
+
+static int __devinit i8042_setup_aux(void)
+{
+       int (*aux_enable)(void);
+       int error;
+       int i;
 
-       if (i8042_controller_init())
+       if (i8042_check_aux())
                return -ENODEV;
 
-       if (!i8042_noaux && !i8042_check_aux()) {
-               if (!i8042_nomux && !i8042_check_mux()) {
-                       for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
-                               err = i8042_create_mux_port(i);
-                               if (err)
-                                       goto err_unregister_ports;
-                       }
-               } else {
-                       err = i8042_create_aux_port();
-                       if (err)
-                               goto err_unregister_ports;
+       if (i8042_nomux || i8042_check_mux()) {
+               error = i8042_create_aux_port(-1);
+               if (error)
+                       goto err_free_ports;
+               aux_enable = i8042_enable_aux_port;
+       } else {
+               for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
+                       error = i8042_create_aux_port(i);
+                       if (error)
+                               goto err_free_ports;
                }
-               have_ports = 1;
+               aux_enable = i8042_enable_mux_ports;
        }
 
-       if (!i8042_nokbd) {
-               err = i8042_create_kbd_port();
-               if (err)
-                       goto err_unregister_ports;
-               have_ports = 1;
-       }
+       error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED,
+                           "i8042", i8042_platform_device);
+       if (error)
+               goto err_free_ports;
 
-       if (!have_ports) {
-               err = -ENODEV;
-               goto err_controller_cleanup;
-       }
+       if (aux_enable())
+               goto err_free_irq;
 
-       mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
+       i8042_aux_irq_registered = 1;
        return 0;
 
- err_unregister_ports:
-       for (i = 0; i < I8042_NUM_PORTS; i++)
-               if (i8042_ports[i].serio)
-                       serio_unregister_port(i8042_ports[i].serio);
- err_controller_cleanup:
-       i8042_controller_cleanup();
+ err_free_irq:
+       free_irq(I8042_AUX_IRQ, i8042_platform_device);
+ err_free_ports:
+       i8042_free_aux_ports();
+       return error;
+}
 
-       return err;
+static int __devinit i8042_setup_kbd(void)
+{
+       int error;
+
+       error = i8042_create_kbd_port();
+       if (error)
+               return error;
+
+       error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
+                           "i8042", i8042_platform_device);
+       if (error)
+               goto err_free_port;
+
+       error = i8042_enable_kbd_port();
+       if (error)
+               goto err_free_irq;
+
+       i8042_kbd_irq_registered = 1;
+       return 0;
+
+ err_free_irq:
+       free_irq(I8042_KBD_IRQ, i8042_platform_device);
+ err_free_port:
+       i8042_free_kbd_port();
+       return error;
 }
 
-static int __devexit i8042_remove(struct platform_device *dev)
+static int __devinit i8042_probe(struct platform_device *dev)
 {
-       int i;
+       int error;
 
-       i8042_controller_cleanup();
+       error = i8042_controller_selftest();
+       if (error)
+               return error;
 
-       for (i = 0; i < I8042_NUM_PORTS; i++)
-               if (i8042_ports[i].exists)
-                       serio_unregister_port(i8042_ports[i].serio);
+       error = i8042_controller_init();
+       if (error)
+               return error;
+
+       if (!i8042_noaux) {
+               error = i8042_setup_aux();
+               if (error && error != -ENODEV && error != -EBUSY)
+                       goto out_fail;
+       }
+
+       if (!i8042_nokbd) {
+               error = i8042_setup_kbd();
+               if (error)
+                       goto out_fail;
+       }
 
-       del_timer_sync(&i8042_timer);
+/*
+ * Ok, everything is ready, let's register all serio ports
+ */
+       i8042_register_ports();
+
+       return 0;
+
+ out_fail:
+       i8042_free_aux_ports(); /* in case KBD failed but AUX not */
+       i8042_free_irqs();
+       i8042_controller_reset();
+
+       return error;
+}
+
+static int __devexit i8042_remove(struct platform_device *dev)
+{
+       i8042_unregister_ports();
+       i8042_free_irqs();
+       i8042_controller_reset();
 
        return 0;
 }
@@ -1134,8 +1167,9 @@ static int __init i8042_init(void)
        if (err)
                return err;
 
-       i8042_ports[I8042_AUX_PORT_NO].irq = I8042_AUX_IRQ;
-       i8042_ports[I8042_KBD_PORT_NO].irq = I8042_KBD_IRQ;
+       err = i8042_controller_check();
+       if (err)
+               goto err_platform_exit;
 
        err = platform_driver_register(&i8042_driver);
        if (err)
@@ -1151,6 +1185,8 @@ static int __init i8042_init(void)
        if (err)
                goto err_free_device;
 
+       panic_blink = i8042_panic_blink;
+
        return 0;
 
  err_free_device:
@@ -1167,7 +1203,6 @@ static void __exit i8042_exit(void)
 {
        platform_device_unregister(i8042_platform_device);
        platform_driver_unregister(&i8042_driver);
-
        i8042_platform_exit();
 
        panic_blink = NULL;
index af526ab9ec04205239f5d580aec7ce30323dc9e2..b3eb7a72d96114653799339dce4d9040391f4abd 100644 (file)
 
 #define I8042_CTL_TIMEOUT      10000
 
-/*
- * When the device isn't opened and it's interrupts aren't used, we poll it at
- * regular intervals to see if any characters arrived. If yes, we can start
- * probing for any mouse / keyboard connected. This is the period of the
- * polling.
- */
-
-#define I8042_POLL_PERIOD      HZ/20
-
 /*
  * Status register bits.
  */
index ed202f2f251aa76eeb3b680186a7baa7ddefaa7a..dcb16b5cbec084716050232f88c16fc53942b19f 100644 (file)
@@ -27,15 +27,6 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
 MODULE_DESCRIPTION("PS/2 driver library");
 MODULE_LICENSE("GPL");
 
-EXPORT_SYMBOL(ps2_init);
-EXPORT_SYMBOL(ps2_sendbyte);
-EXPORT_SYMBOL(ps2_drain);
-EXPORT_SYMBOL(ps2_command);
-EXPORT_SYMBOL(ps2_schedule_command);
-EXPORT_SYMBOL(ps2_handle_ack);
-EXPORT_SYMBOL(ps2_handle_response);
-EXPORT_SYMBOL(ps2_cmd_aborted);
-
 /* Work structure to schedule execution of a command */
 struct ps2work {
        struct work_struct work;
@@ -71,6 +62,7 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
 
        return -ps2dev->nak;
 }
+EXPORT_SYMBOL(ps2_sendbyte);
 
 /*
  * ps2_drain() waits for device to transmit requested number of bytes
@@ -96,15 +88,16 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
                           msecs_to_jiffies(timeout));
        mutex_unlock(&ps2dev->cmd_mutex);
 }
+EXPORT_SYMBOL(ps2_drain);
 
 /*
  * ps2_is_keyboard_id() checks received ID byte against the list of
  * known keyboard IDs.
  */
 
-static inline int ps2_is_keyboard_id(char id_byte)
+int ps2_is_keyboard_id(char id_byte)
 {
-       static char keyboard_ids[] = {
+       const static char keyboard_ids[] = {
                0xab,   /* Regular keyboards            */
                0xac,   /* NCD Sun keyboard             */
                0x2b,   /* Trust keyboard, translated   */
@@ -115,6 +108,7 @@ static inline int ps2_is_keyboard_id(char id_byte)
 
        return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL;
 }
+EXPORT_SYMBOL(ps2_is_keyboard_id);
 
 /*
  * ps2_adjust_timeout() is called after receiving 1st byte of command
@@ -138,6 +132,19 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
                        break;
 
                case PS2_CMD_GETID:
+                       /*
+                        * Microsoft Natural Elite keyboard responds to
+                        * the GET ID command as it were a mouse, with
+                        * a single byte. Fail the command so atkbd will
+                        * use alternative probe to detect it.
+                        */
+                       if (ps2dev->cmdbuf[1] == 0xaa) {
+                               serio_pause_rx(ps2dev->serio);
+                               ps2dev->flags = 0;
+                               serio_continue_rx(ps2dev->serio);
+                               timeout = 0;
+                       }
+
                        /*
                         * If device behind the port is not a keyboard there
                         * won't be 2nd byte of ID response.
@@ -237,6 +244,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
        mutex_unlock(&ps2dev->cmd_mutex);
        return rc;
 }
+EXPORT_SYMBOL(ps2_command);
 
 /*
  * ps2_execute_scheduled_command() sends a command, previously scheduled by
@@ -279,6 +287,7 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman
 
        return 0;
 }
+EXPORT_SYMBOL(ps2_schedule_command);
 
 /*
  * ps2_init() initializes ps2dev structure
@@ -290,6 +299,7 @@ void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
        init_waitqueue_head(&ps2dev->wait);
        ps2dev->serio = serio;
 }
+EXPORT_SYMBOL(ps2_init);
 
 /*
  * ps2_handle_ack() is supposed to be used in interrupt handler
@@ -335,6 +345,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
 
        return 1;
 }
+EXPORT_SYMBOL(ps2_handle_ack);
 
 /*
  * ps2_handle_response() is supposed to be used in interrupt handler
@@ -360,6 +371,7 @@ int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
 
        return 1;
 }
+EXPORT_SYMBOL(ps2_handle_response);
 
 void ps2_cmd_aborted(struct ps2dev *ps2dev)
 {
@@ -371,4 +383,4 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev)
 
        ps2dev->flags = 0;
 }
-
+EXPORT_SYMBOL(ps2_cmd_aborted);
index b1b14f8d4dd6ecf8b6b5e1675cc7c67c7d7b4e6c..9418bbe4707298bc1d605fca696c7e03790b8f5d 100644 (file)
@@ -108,4 +108,40 @@ config TOUCHSCREEN_HP600
          To compile this driver as a module, choose M here: the
          module will be called hp680_ts_input.
 
+config TOUCHSCREEN_PENMOUNT
+       tristate "Penmount serial touchscreen"
+       select SERIO
+       help
+         Say Y here if you have a Penmount serial touchscreen connected to
+         your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called penmount.
+
+config TOUCHSCREEN_TOUCHRIGHT
+       tristate "Touchright serial touchscreen"
+       select SERIO
+       help
+         Say Y here if you have a Touchright serial touchscreen connected to
+         your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called touchright.
+
+config TOUCHSCREEN_TOUCHWIN
+       tristate "Touchwin serial touchscreen"
+       select SERIO
+       help
+         Say Y here if you have a Touchwin serial touchscreen connected to
+         your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called touchwin.
+
 endif
index 5e5557c43121a128243412074d689c8340a5f2fd..1abb8f10d608809f5ecb57d4b0b1563cb3744bec 100644 (file)
@@ -12,3 +12,6 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
 obj-$(CONFIG_TOUCHSCREEN_MK712)        += mk712.o
 obj-$(CONFIG_TOUCHSCREEN_HP600)        += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)     += penmount.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)     += touchwin.o
index c86a2eb310fd93703f30246a7094d18a5529b64f..ab565335ee44131a69830fc9ac47b1213f58ce14 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/input.h>
 #include <linux/serio.h>
 #include <linux/init.h>
+#include <linux/ctype.h>
 
 #define DRIVER_DESC    "Elo serial touchscreen driver"
 
@@ -34,7 +35,19 @@ MODULE_LICENSE("GPL");
  * Definitions & global arrays.
  */
 
-#define        ELO_MAX_LENGTH  10
+#define ELO_MAX_LENGTH         10
+
+#define ELO10_PACKET_LEN       8
+#define ELO10_TOUCH            0x03
+#define ELO10_PRESSURE         0x80
+
+#define ELO10_LEAD_BYTE                'U'
+
+#define ELO10_ID_CMD           'i'
+
+#define ELO10_TOUCH_PACKET     'T'
+#define ELO10_ACK_PACKET       'A'
+#define ELI10_ID_PACKET                'I'
 
 /*
  * Per-touchscreen data.
@@ -43,51 +56,67 @@ MODULE_LICENSE("GPL");
 struct elo {
        struct input_dev *dev;
        struct serio *serio;
+       struct mutex cmd_mutex;
+       struct completion cmd_done;
        int id;
        int idx;
+       unsigned char expected_packet;
        unsigned char csum;
        unsigned char data[ELO_MAX_LENGTH];
+       unsigned char response[ELO10_PACKET_LEN];
        char phys[32];
 };
 
-static void elo_process_data_10(struct eloelo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_10(struct elo *elo, unsigned char data, struct pt_regs *regs)
 {
        struct input_dev *dev = elo->dev;
 
-       elo->csum += elo->data[elo->idx] = data;
-
+       elo->data[elo->idx] = data;
        switch (elo->idx++) {
-
                case 0:
-                       if (data != 'U') {
+                       elo->csum = 0xaa;
+                       if (data != ELO10_LEAD_BYTE) {
+                               pr_debug("elo: unsynchronized data: 0x%02x\n", data);
                                elo->idx = 0;
-                               elo->csum = 0;
-                       }
-                       break;
-
-               case 1:
-                       if (data != 'T') {
-                               elo->idx = 0;
-                               elo->csum = 0;
                        }
                        break;
 
                case 9:
-                       if (elo->csum) {
+                       elo->idx = 0;
+                       if (data != elo->csum) {
+                               pr_debug("elo: bad checksum: 0x%02x, expected 0x%02x\n",
+                                        data, elo->csum);
+                               break;
+                       }
+                       if (elo->data[1] != elo->expected_packet) {
+                               if (elo->data[1] != ELO10_TOUCH_PACKET)
+                                       pr_debug("elo: unexpected packet: 0x%02x\n",
+                                                elo->data[1]);
+                               break;
+                       }
+                       if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) {
                                input_regs(dev, regs);
                                input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
                                input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
-                               input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]);
-                               input_report_key(dev, BTN_TOUCH, elo->data[8] || elo->data[7]);
+                               if (elo->data[2] & ELO10_PRESSURE)
+                                       input_report_abs(dev, ABS_PRESSURE,
+                                                       (elo->data[8] << 8) | elo->data[7]);
+                               input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH);
                                input_sync(dev);
+                       } else if (elo->data[1] == ELO10_ACK_PACKET) {
+                               if (elo->data[2] == '0')
+                                       elo->expected_packet = ELO10_TOUCH_PACKET;
+                               complete(&elo->cmd_done);
+                       } else {
+                               memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN);
+                               elo->expected_packet = ELO10_ACK_PACKET;
                        }
-                       elo->idx = 0;
-                       elo->csum = 0;
                        break;
        }
+       elo->csum += data;
 }
 
-static void elo_process_data_6(struct eloelo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_6(struct elo *elo, unsigned char data, struct pt_regs *regs)
 {
        struct input_dev *dev = elo->dev;
 
@@ -135,7 +164,7 @@ static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_re
        }
 }
 
-static void elo_process_data_3(struct eloelo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_3(struct elo *elo, unsigned char data, struct pt_regs *regs)
 {
        struct input_dev *dev = elo->dev;
 
@@ -161,7 +190,7 @@ static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_re
 static irqreturn_t elo_interrupt(struct serio *serio,
                unsigned char data, unsigned int flags, struct pt_regs *regs)
 {
-       struct eloelo = serio_get_drvdata(serio);
+       struct elo *elo = serio_get_drvdata(serio);
 
        switch(elo->id) {
                case 0:
@@ -181,17 +210,81 @@ static irqreturn_t elo_interrupt(struct serio *serio,
        return IRQ_HANDLED;
 }
 
+static int elo_command_10(struct elo *elo, unsigned char *packet)
+{
+       int rc = -1;
+       int i;
+       unsigned char csum = 0xaa + ELO10_LEAD_BYTE;
+
+       mutex_lock(&elo->cmd_mutex);
+
+       serio_pause_rx(elo->serio);
+       elo->expected_packet = toupper(packet[0]);
+       init_completion(&elo->cmd_done);
+       serio_continue_rx(elo->serio);
+
+       if (serio_write(elo->serio, ELO10_LEAD_BYTE))
+               goto out;
+
+       for (i = 0; i < ELO10_PACKET_LEN; i++) {
+               csum += packet[i];
+               if (serio_write(elo->serio, packet[i]))
+                       goto out;
+       }
+
+       if (serio_write(elo->serio, csum))
+               goto out;
+
+       wait_for_completion_timeout(&elo->cmd_done, HZ);
+
+       if (elo->expected_packet == ELO10_TOUCH_PACKET) {
+               /* We are back in reporting mode, the command was ACKed */
+               memcpy(packet, elo->response, ELO10_PACKET_LEN);
+               rc = 0;
+       }
+
+ out:
+       mutex_unlock(&elo->cmd_mutex);
+       return rc;
+}
+
+static int elo_setup_10(struct elo *elo)
+{
+       static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
+       struct input_dev *dev = elo->dev;
+       unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
+
+       if (elo_command_10(elo, packet))
+               return -1;
+
+       dev->id.version = (packet[5] << 8) | packet[4];
+
+       input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0);
+       input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0);
+       if (packet[3] & ELO10_PRESSURE)
+               input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+       printk(KERN_INFO "elo: %sTouch touchscreen, fw: %02x.%02x, "
+               "features: %x02x, controller: 0x%02x\n",
+               elo_types[(packet[1] -'0') & 0x03],
+               packet[5], packet[4], packet[3], packet[7]);
+
+       return 0;
+}
+
 /*
  * elo_disconnect() is the opposite of elo_connect()
  */
 
 static void elo_disconnect(struct serio *serio)
 {
-       struct eloelo = serio_get_drvdata(serio);
+       struct elo *elo = serio_get_drvdata(serio);
 
+       input_get_device(elo->dev);
        input_unregister_device(elo->dev);
        serio_close(serio);
        serio_set_drvdata(serio, NULL);
+       input_put_device(elo->dev);
        kfree(elo);
 }
 
@@ -211,12 +304,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
        input_dev = input_allocate_device();
        if (!elo || !input_dev) {
                err = -ENOMEM;
-               goto fail;
+               goto fail1;
        }
 
        elo->serio = serio;
        elo->id = serio->id.id;
        elo->dev = input_dev;
+       elo->expected_packet = ELO10_TOUCH_PACKET;
+       mutex_init(&elo->cmd_mutex);
+       init_completion(&elo->cmd_done);
        snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys);
 
        input_dev->private = elo;
@@ -231,12 +327,17 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
        input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
        input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
 
+       serio_set_drvdata(serio, elo);
+       err = serio_open(serio, drv);
+       if (err)
+               goto fail2;
+
        switch (elo->id) {
 
                case 0: /* 10-byte protocol */
-                       input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
-                       input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0);
-                       input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+                       if (elo_setup_10(elo))
+                               goto fail3;
+
                        break;
 
                case 1: /* 6-byte protocol */
@@ -253,17 +354,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
                        break;
        }
 
-       serio_set_drvdata(serio, elo);
-
-       err = serio_open(serio, drv);
+       err = input_register_device(elo->dev);
        if (err)
-               goto fail;
+               goto fail3;
 
-       input_register_device(elo->dev);
        return 0;
 
- fail: serio_set_drvdata(serio, NULL);
-       input_free_device(input_dev);
+ fail3: serio_close(serio);
+ fail2:        serio_set_drvdata(serio, NULL);
+ fail1:        input_free_device(input_dev);
        kfree(elo);
        return err;
 }
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
new file mode 100644 (file)
index 0000000..f737010
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Penmount serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on ELO driver (drivers/input/touchscreen/elo.c)
+ * Copyright (c) 2004 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC    "Penmount serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define        PM_MAX_LENGTH   5
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct pm {
+       struct input_dev *dev;
+       struct serio *serio;
+       int idx;
+       unsigned char data[PM_MAX_LENGTH];
+       char phys[32];
+};
+
+static irqreturn_t pm_interrupt(struct serio *serio,
+               unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+       struct pm *pm = serio_get_drvdata(serio);
+       struct input_dev *dev = pm->dev;
+
+       pm->data[pm->idx] = data;
+
+       if (pm->data[0] & 0x80) {
+               if (PM_MAX_LENGTH == ++pm->idx) {
+                       input_regs(dev, regs);
+                       input_report_abs(dev, ABS_X, pm->data[2] * 128 + pm->data[1]);
+                       input_report_abs(dev, ABS_Y, pm->data[4] * 128 + pm->data[3]);
+                       input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
+                       input_sync(dev);
+                       pm->idx = 0;
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * pm_disconnect() is the opposite of pm_connect()
+ */
+
+static void pm_disconnect(struct serio *serio)
+{
+       struct pm *pm = serio_get_drvdata(serio);
+
+       input_get_device(pm->dev);
+       input_unregister_device(pm->dev);
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_put_device(pm->dev);
+       kfree(pm);
+}
+
+/*
+ * pm_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int pm_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct pm *pm;
+       struct input_dev *input_dev;
+       int err;
+
+       pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!pm || !input_dev) {
+               err = -ENOMEM;
+               goto fail1;
+       }
+
+       pm->serio = serio;
+       pm->dev = input_dev;
+       snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
+
+       input_dev->private = pm;
+       input_dev->name = "Penmount Serial TouchScreen";
+       input_dev->phys = pm->phys;
+       input_dev->id.bustype = BUS_RS232;
+       input_dev->id.vendor = SERIO_PENMOUNT;
+       input_dev->id.product = 0;
+       input_dev->id.version = 0x0100;
+       input_dev->cdev.dev = &serio->dev;
+
+        input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+        input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+        input_set_abs_params(pm->dev, ABS_X, 0, 0x3ff, 0, 0);
+        input_set_abs_params(pm->dev, ABS_Y, 0, 0x3ff, 0, 0);
+
+       serio_set_drvdata(serio, pm);
+
+       err = serio_open(serio, drv);
+       if (err)
+               goto fail2;
+
+       err = input_register_device(pm->dev);
+       if (err)
+               goto fail3;
+
+       return 0;
+
+ fail3:        serio_close(serio);
+ fail2:        serio_set_drvdata(serio, NULL);
+ fail1:        input_free_device(input_dev);
+       kfree(pm);
+       return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id pm_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_PENMOUNT,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, pm_serio_ids);
+
+static struct serio_driver pm_drv = {
+       .driver         = {
+               .name   = "penmountlpc",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = pm_serio_ids,
+       .interrupt      = pm_interrupt,
+       .connect        = pm_connect,
+       .disconnect     = pm_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init pm_init(void)
+{
+       serio_register_driver(&pm_drv);
+       return 0;
+}
+
+static void __exit pm_exit(void)
+{
+       serio_unregister_driver(&pm_drv);
+}
+
+module_init(pm_init);
+module_exit(pm_exit);
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
new file mode 100644 (file)
index 0000000..1c89fa5
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Touchright serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC    "Touchright serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TR_FORMAT_TOUCH_BIT    0x01
+#define TR_FORMAT_STATUS_BYTE  0x40
+#define TR_FORMAT_STATUS_MASK  ~TR_FORMAT_TOUCH_BIT
+
+#define TR_LENGTH 5
+
+#define TR_MIN_XC 0
+#define TR_MAX_XC 0x1ff
+#define TR_MIN_YC 0
+#define TR_MAX_YC 0x1ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tr {
+       struct input_dev *dev;
+       struct serio *serio;
+       int idx;
+       unsigned char data[TR_LENGTH];
+       char phys[32];
+};
+
+static irqreturn_t tr_interrupt(struct serio *serio,
+               unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+       struct tr *tr = serio_get_drvdata(serio);
+       struct input_dev *dev = tr->dev;
+
+       tr->data[tr->idx] = data;
+
+       if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) {
+               if (++tr->idx == TR_LENGTH) {
+                       input_regs(dev, regs);
+                       input_report_abs(dev, ABS_X,
+                               (tr->data[1] << 5) | (tr->data[2] >> 1));
+                       input_report_abs(dev, ABS_Y,
+                               (tr->data[3] << 5) | (tr->data[4] >> 1));
+                       input_report_key(dev, BTN_TOUCH,
+                               tr->data[0] & TR_FORMAT_TOUCH_BIT);
+                       input_sync(dev);
+                       tr->idx = 0;
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * tr_disconnect() is the opposite of tr_connect()
+ */
+
+static void tr_disconnect(struct serio *serio)
+{
+       struct tr *tr = serio_get_drvdata(serio);
+
+       input_get_device(tr->dev);
+       input_unregister_device(tr->dev);
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_put_device(tr->dev);
+       kfree(tr);
+}
+
+/*
+ * tr_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int tr_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct tr *tr;
+       struct input_dev *input_dev;
+       int err;
+
+       tr = kzalloc(sizeof(struct tr), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!tr || !input_dev) {
+               err = -ENOMEM;
+               goto fail1;
+       }
+
+       tr->serio = serio;
+       tr->dev = input_dev;
+       snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys);
+
+       input_dev->private = tr;
+       input_dev->name = "Touchright Serial TouchScreen";
+       input_dev->phys = tr->phys;
+       input_dev->id.bustype = BUS_RS232;
+       input_dev->id.vendor = SERIO_TOUCHRIGHT;
+       input_dev->id.product = 0;
+       input_dev->id.version = 0x0100;
+       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+       input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0);
+       input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0);
+
+       serio_set_drvdata(serio, tr);
+
+       err = serio_open(serio, drv);
+       if (err)
+               goto fail2;
+
+       err = input_register_device(tr->dev);
+       if (err)
+               goto fail3;
+
+       return 0;
+
+ fail3:        serio_close(serio);
+ fail2:        serio_set_drvdata(serio, NULL);
+ fail1:        input_free_device(input_dev);
+       kfree(tr);
+       return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tr_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_TOUCHRIGHT,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tr_serio_ids);
+
+static struct serio_driver tr_drv = {
+       .driver         = {
+               .name   = "touchright",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = tr_serio_ids,
+       .interrupt      = tr_interrupt,
+       .connect        = tr_connect,
+       .disconnect     = tr_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init tr_init(void)
+{
+       serio_register_driver(&tr_drv);
+       return 0;
+}
+
+static void __exit tr_exit(void)
+{
+       serio_unregister_driver(&tr_drv);
+}
+
+module_init(tr_init);
+module_exit(tr_exit);
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
new file mode 100644 (file)
index 0000000..a7b4c75
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Touchwindow serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2005/02/19 Rick Koch:
+ *   The Touchwindow I used is made by Edmark Corp. and
+ *   constantly outputs a stream of 0's unless it is touched.
+ *   It then outputs 3 bytes: X, Y, and a copy of Y.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC    "Touchwindow serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TW_LENGTH 3
+
+#define TW_MIN_XC 0
+#define TW_MAX_XC 0xff
+#define TW_MIN_YC 0
+#define TW_MAX_YC 0xff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tw {
+       struct input_dev *dev;
+       struct serio *serio;
+       int idx;
+       int touched;
+       unsigned char data[TW_LENGTH];
+       char phys[32];
+};
+
+static irqreturn_t tw_interrupt(struct serio *serio,
+               unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+       struct tw *tw = serio_get_drvdata(serio);
+       struct input_dev *dev = tw->dev;
+
+       if (data) {             /* touch */
+               tw->touched = 1;
+               tw->data[tw->idx++] = data;
+               /* verify length and that the two Y's are the same */
+               if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
+                       input_regs(dev, regs);
+                       input_report_abs(dev, ABS_X, tw->data[0]);
+                       input_report_abs(dev, ABS_Y, tw->data[1]);
+                       input_report_key(dev, BTN_TOUCH, 1);
+                       input_sync(dev);
+                       tw->idx = 0;
+               }
+       } else if (tw->touched) {       /* untouch */
+               input_report_key(dev, BTN_TOUCH, 0);
+               input_sync(dev);
+               tw->idx = 0;
+               tw->touched = 0;
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * tw_disconnect() is the opposite of tw_connect()
+ */
+
+static void tw_disconnect(struct serio *serio)
+{
+       struct tw *tw = serio_get_drvdata(serio);
+
+       input_get_device(tw->dev);
+       input_unregister_device(tw->dev);
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_put_device(tw->dev);
+       kfree(tw);
+}
+
+/*
+ * tw_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchwin protocol and registers it as
+ * an input device.
+ */
+
+static int tw_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct tw *tw;
+       struct input_dev *input_dev;
+       int err;
+
+       tw = kzalloc(sizeof(struct tw), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!tw || !input_dev) {
+               err = -ENOMEM;
+               goto fail1;
+       }
+
+       tw->serio = serio;
+       tw->dev = input_dev;
+       snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys);
+
+       input_dev->private = tw;
+       input_dev->name = "Touchwindow Serial TouchScreen";
+       input_dev->phys = tw->phys;
+       input_dev->id.bustype = BUS_RS232;
+       input_dev->id.vendor = SERIO_TOUCHWIN;
+       input_dev->id.product = 0;
+       input_dev->id.version = 0x0100;
+       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+       input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0);
+       input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0);
+
+       serio_set_drvdata(serio, tw);
+
+       err = serio_open(serio, drv);
+       if (err)
+               goto fail2;
+
+       err = input_register_device(tw->dev);
+       if (err)
+               goto fail3;
+
+       return 0;
+
+ fail3:        serio_close(serio);
+ fail2:        serio_set_drvdata(serio, NULL);
+ fail1:        input_free_device(input_dev);
+       kfree(tw);
+       return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tw_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_TOUCHWIN,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tw_serio_ids);
+
+static struct serio_driver tw_drv = {
+       .driver         = {
+               .name   = "touchwin",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = tw_serio_ids,
+       .interrupt      = tw_interrupt,
+       .connect        = tw_connect,
+       .disconnect     = tw_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init tw_init(void)
+{
+       serio_register_driver(&tw_drv);
+       return 0;
+}
+
+static void __exit tw_exit(void)
+{
+       serio_unregister_driver(&tw_drv);
+}
+
+module_init(tw_init);
+module_exit(tw_exit);
index 00e3929c62888a906852558cf7935cf9b6c36022..a730c461227f78608d8c461195f33b80f11210fb 100644 (file)
@@ -135,8 +135,6 @@ struct tsdev_list {
 #define TS_GET_CAL     _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration)
 #define TS_SET_CAL     _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
 
-static struct input_handler tsdev_handler;
-
 static struct tsdev *tsdev_table[TSDEV_MINORS/2];
 
 static int tsdev_fasync(int fd, struct file *file, int on)
@@ -263,7 +261,7 @@ static int tsdev_ioctl(struct inode *inode, struct file *file,
        return retval;
 }
 
-static struct file_operations tsdev_fops = {
+static const struct file_operations tsdev_fops = {
        .owner =        THIS_MODULE,
        .open =         tsdev_open,
        .release =      tsdev_release,
@@ -370,7 +368,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
 
 static struct input_handle *tsdev_connect(struct input_handler *handler,
                                          struct input_dev *dev,
-                                         struct input_device_id *id)
+                                         const struct input_device_id *id)
 {
        struct tsdev *tsdev;
        struct class_device *cdev;
@@ -443,7 +441,7 @@ static void tsdev_disconnect(struct input_handle *handle)
                tsdev_free(tsdev);
 }
 
-static struct input_device_id tsdev_ids[] = {
+static const struct input_device_id tsdev_ids[] = {
        {
              .flags    = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
              .evbit    = { BIT(EV_KEY) | BIT(EV_REL) },
@@ -481,9 +479,7 @@ static struct input_handler tsdev_handler = {
 
 static int __init tsdev_init(void)
 {
-       input_register_handler(&tsdev_handler);
-       printk(KERN_INFO "ts: Compaq touchscreen protocol output\n");
-       return 0;
+       return input_register_handler(&tsdev_handler);
 }
 
 static void __exit tsdev_exit(void)
index 650103bc9618434ad769972ebb6db5e8cf76ae01..7ec35c90d2d739bbe381b06c47997bc85712a908 100644 (file)
@@ -60,16 +60,17 @@ config HID_FF
          If unsure, say N.
 
 config HID_PID
-       bool "PID Devices (Microsoft Sidewinder Force Feedback 2)"
+       bool "PID device support"
        depends on HID_FF
        help
-         Say Y here if you have a PID-compliant joystick and wish to enable force
-         feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such
-         device.
+         Say Y here if you have a PID-compliant device and wish to enable force
+         feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
+         devices.
 
 config LOGITECH_FF
        bool "Logitech WingMan *3D support"
        depends on HID_FF
+       select INPUT_FF_MEMLESS if USB_HID
        help
          Say Y here if you have one of these devices:
          - Logitech WingMan Cordless RumblePad
@@ -81,12 +82,21 @@ config LOGITECH_FF
 config THRUSTMASTER_FF
        bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
        depends on HID_FF && EXPERIMENTAL
+       select INPUT_FF_MEMLESS if USB_HID
        help
          Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
          and want to enable force feedback support for it.
          Note: if you say N here, this device will still be supported, but without
          force feedback.
 
+config ZEROPLUS_FF
+       bool "Zeroplus based game controller support"
+       depends on HID_FF
+       select INPUT_FF_MEMLESS if USB_HID
+       help
+         Say Y here if you have a Zeroplus based game controller and want to
+         enable force feedback for it.
+
 config USB_HIDDEV
        bool "/dev/hiddev raw HID device support"
        depends on USB_HID
index 764114529c56af10071d2dfa94f21717edf34e18..affb6a36d242eb4331d6c5fbe29f35f7a7e85c8c 100644 (file)
@@ -14,7 +14,7 @@ ifeq ($(CONFIG_USB_HIDINPUT),y)
        usbhid-objs     += hid-input.o
 endif
 ifeq ($(CONFIG_HID_PID),y)
-       usbhid-objs     += pid.o
+       usbhid-objs     += hid-pidff.o
 endif
 ifeq ($(CONFIG_LOGITECH_FF),y)
        usbhid-objs     += hid-lgff.o
@@ -22,6 +22,9 @@ endif
 ifeq ($(CONFIG_THRUSTMASTER_FF),y)
        usbhid-objs     += hid-tmff.o
 endif
+ifeq ($(CONFIG_ZEROPLUS_FF),y)
+       usbhid-objs     += hid-zpff.o
+endif
 ifeq ($(CONFIG_HID_FF),y)
        usbhid-objs     += hid-ff.o
 endif
index a2c56b2de58984d901340ebc01bb639f25c9fc01..bfa267722f1bef96be8e780b5c0a4ae82bb35d98 100644 (file)
@@ -543,8 +543,6 @@ static void hid_free_device(struct hid_device *device)
 {
        unsigned i,j;
 
-       hid_ff_exit(device);
-
        for (i = 0; i < HID_REPORT_TYPES; i++) {
                struct hid_report_enum *report_enum = device->report_enum + i;
 
@@ -1108,7 +1106,7 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
 /*
  * Find a report field with a specified HID usage.
  */
-
+#if 0
 struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type)
 {
        struct hid_report *report;
@@ -1120,6 +1118,7 @@ struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_u
                                return report->field[i];
        return NULL;
 }
+#endif  /*  0  */
 
 static int hid_submit_out(struct hid_device *hid)
 {
index d5c91ee67991cf9e4331ad451cca18ca8e67a2c4..a8fc46c721c50cf0a1a8485d50da96c800b3f5fe 100644 (file)
@@ -44,45 +44,38 @@ struct hid_ff_initializer {
        int (*init)(struct hid_device*);
 };
 
+/*
+ * We try pidff when no other driver is found because PID is the
+ * standards compliant way of implementing force feedback in HID.
+ * pidff_init() will quickly abort if the device doesn't appear to
+ * be a PID device
+ */
 static struct hid_ff_initializer inits[] = {
 #ifdef CONFIG_LOGITECH_FF
-       {0x46d, 0xc211, hid_lgff_init}, // Logitech Cordless rumble pad
-       {0x46d, 0xc283, hid_lgff_init}, // Logitech Wingman Force 3d
-       {0x46d, 0xc295, hid_lgff_init}, // Logitech MOMO force wheel
-       {0x46d, 0xc219, hid_lgff_init}, // Logitech Cordless rumble pad 2
-#endif
-#ifdef CONFIG_HID_PID
-       {0x45e, 0x001b, hid_pid_init},
+       { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
+       { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
+       { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
+       { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
 #endif
 #ifdef CONFIG_THRUSTMASTER_FF
-       {0x44f, 0xb304, hid_tmff_init},
+       { 0x44f, 0xb304, hid_tmff_init },
 #endif
-       {0, 0, NULL} /* Terminating entry */
+#ifdef CONFIG_ZEROPLUS_FF
+       { 0xc12, 0x0005, hid_zpff_init },
+       { 0xc12, 0x0030, hid_zpff_init },
+#endif
+       { 0,     0,      hid_pidff_init}  /* Matches anything */
 };
 
-static struct hid_ff_initializer *hid_get_ff_init(__u16 idVendor,
-                                                 __u16 idProduct)
-{
-       struct hid_ff_initializer *init;
-       for (init = inits;
-            init->idVendor
-            && !(init->idVendor == idVendor
-                 && init->idProduct == idProduct);
-            init++);
-
-       return init->idVendor? init : NULL;
-}
-
 int hid_ff_init(struct hid_device* hid)
 {
        struct hid_ff_initializer *init;
+       int vendor = le16_to_cpu(hid->dev->descriptor.idVendor);
+       int product = le16_to_cpu(hid->dev->descriptor.idProduct);
 
-       init = hid_get_ff_init(le16_to_cpu(hid->dev->descriptor.idVendor),
-                              le16_to_cpu(hid->dev->descriptor.idProduct));
+       for (init = inits; init->idVendor; init++)
+               if (init->idVendor == vendor && init->idProduct == product)
+                       break;
 
-       if (!init) {
-               dbg("hid_ff_init could not find initializer");
-               return -ENOSYS;
-       }
        return init->init(hid);
 }
index 7208839f2dbf24d50c15516b7d99971980f8160d..4c62afbeb430a31620ff3681d6ac303b613af187 100644 (file)
@@ -65,11 +65,9 @@ static const struct {
 #define map_rel(c)     do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)     do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
 #define map_led(c)     do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0)
-#define map_ff(c)      do { usage->code = c; usage->type = EV_FF;  bit = input->ffbit;  max =  FF_MAX; } while (0)
 
 #define map_abs_clear(c)       do { map_abs(c); clear_bit(c, bit); } while (0)
 #define map_key_clear(c)       do { map_key(c); clear_bit(c, bit); } while (0)
-#define map_ff_effect(c)       do { set_bit(c, input->ffbit); } while (0)
 
 #ifdef CONFIG_USB_HIDINPUT_POWERBOOK
 
@@ -525,23 +523,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
 
                case HID_UP_PID:
 
-                       set_bit(EV_FF, input->evbit);
                        switch(usage->hid & HID_USAGE) {
-                               case 0x26: map_ff_effect(FF_CONSTANT);  goto ignore;
-                               case 0x27: map_ff_effect(FF_RAMP);      goto ignore;
-                               case 0x28: map_ff_effect(FF_CUSTOM);    goto ignore;
-                               case 0x30: map_ff_effect(FF_SQUARE);    map_ff_effect(FF_PERIODIC); goto ignore;
-                               case 0x31: map_ff_effect(FF_SINE);      map_ff_effect(FF_PERIODIC); goto ignore;
-                               case 0x32: map_ff_effect(FF_TRIANGLE);  map_ff_effect(FF_PERIODIC); goto ignore;
-                               case 0x33: map_ff_effect(FF_SAW_UP);    map_ff_effect(FF_PERIODIC); goto ignore;
-                               case 0x34: map_ff_effect(FF_SAW_DOWN);  map_ff_effect(FF_PERIODIC); goto ignore;
-                               case 0x40: map_ff_effect(FF_SPRING);    goto ignore;
-                               case 0x41: map_ff_effect(FF_DAMPER);    goto ignore;
-                               case 0x42: map_ff_effect(FF_INERTIA);   goto ignore;
-                               case 0x43: map_ff_effect(FF_FRICTION);  goto ignore;
-                               case 0x7e: map_ff(FF_GAIN);             break;
-                               case 0x83: input->ff_effects_max = field->value[0]; goto ignore;
-                               case 0x98: map_ff(FF_AUTOCENTER);       break;
                                case 0xa4: map_key_clear(BTN_DEAD);     break;
                                default: goto ignore;
                        }
@@ -698,8 +680,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
        }
 
        if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
-               input->ff_effects_max = value;
-               dbg("Maximum Effects - %d",input->ff_effects_max);
+               dbg("Maximum Effects - %d",value);
                return;
        }
 
@@ -748,7 +729,7 @@ static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsign
        int offset;
 
        if (type == EV_FF)
-               return hid_ff_event(hid, dev, type, code, value);
+               return input_ff_event(dev, type, code, value);
 
        if (type != EV_LED)
                return -1;
index f07d44357ff12fbb01eb18271d0832acbbabe672..93da222b6da8ffce8ab4d11762ab562e2d4fc2d1 100644 (file)
@@ -1,12 +1,11 @@
 /*
- * $$
- *
  * Force feedback support for hid-compliant for some of the devices from
  * Logitech, namely:
  * - WingMan Cordless RumblePad
  * - WingMan Force 3D
  *
  *  Copyright (c) 2002-2004 Johann Deneux
+ *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
  */
 
 /*
  */
 
 #include <linux/input.h>
-#include <linux/sched.h>
-
-//#define DEBUG
 #include <linux/usb.h>
-
-#include <linux/circ_buf.h>
-
 #include "hid.h"
-#include "fixp-arith.h"
-
-
-/* Periodicity of the update */
-#define PERIOD (HZ/10)
-
-#define RUN_AT(t) (jiffies + (t))
-
-/* Effect status */
-#define EFFECT_STARTED 0     /* Effect is going to play after some time
-                               (ff_replay.delay) */
-#define EFFECT_PLAYING 1     /* Effect is being played */
-#define EFFECT_USED    2
-
-// For lgff_device::flags
-#define DEVICE_CLOSING 0     /* The driver is being unitialised */
-
-/* Check that the current process can access an effect */
-#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
-        || effect.owner == current->pid)
-
-#define LGFF_CHECK_OWNERSHIP(i, l) \
-        (i>=0 && i<LGFF_EFFECTS \
-        && test_bit(EFFECT_USED, l->effects[i].flags) \
-        && CHECK_OWNERSHIP(l->effects[i]))
-
-#define LGFF_EFFECTS 8
 
 struct device_type {
        u16 idVendor;
        u16 idProduct;
-       signed short *ff;
-};
-
-struct lgff_effect {
-       pid_t owner;
-
-       struct ff_effect effect;
-
-       unsigned long flags[1];
-       unsigned int count;          /* Number of times left to play */
-       unsigned long started_at;    /* When the effect started to play */
-};
-
-struct lgff_device {
-       struct hid_device* hid;
-
-       struct hid_report* constant;
-       struct hid_report* rumble;
-       struct hid_report* condition;
-
-       struct lgff_effect effects[LGFF_EFFECTS];
-       spinlock_t lock;             /* device-level lock. Having locks on
-                                       a per-effect basis could be nice, but
-                                       isn't really necessary */
-
-       unsigned long flags[1];      /* Contains various information about the
-                                       state of the driver for this device */
-
-       struct timer_list timer;
+       const signed short *ff;
 };
 
-/* Callbacks */
-static void hid_lgff_exit(struct hid_device* hid);
-static int hid_lgff_event(struct hid_device *hid, struct input_dev *input,
-                         unsigned int type, unsigned int code, int value);
-static int hid_lgff_flush(struct input_dev *input, struct file *file);
-static int hid_lgff_upload_effect(struct input_dev *input,
-                                 struct ff_effect *effect);
-static int hid_lgff_erase(struct input_dev *input, int id);
-
-/* Local functions */
-static void hid_lgff_input_init(struct hid_device* hid);
-static void hid_lgff_timer(unsigned long timer_data);
-static struct hid_report* hid_lgff_duplicate_report(struct hid_report*);
-static void hid_lgff_delete_report(struct hid_report*);
-
-static signed short ff_rumble[] = {
+static const signed short ff_rumble[] = {
        FF_RUMBLE,
        -1
 };
 
-static signed short ff_joystick[] = {
+static const signed short ff_joystick[] = {
        FF_CONSTANT,
        -1
 };
 
-static struct device_type devices[] = {
-       {0x046d, 0xc211, ff_rumble},
-       {0x046d, 0xc219, ff_rumble},
-       {0x046d, 0xc283, ff_joystick},
-       {0x0000, 0x0000, ff_joystick}
+static const struct device_type devices[] = {
+       { 0x046d, 0xc211, ff_rumble },
+       { 0x046d, 0xc219, ff_rumble },
+       { 0x046d, 0xc283, ff_joystick },
+       { 0x0000, 0x0000, ff_joystick }
 };
 
+static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+       struct hid_device *hid = dev->private;
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       int x, y;
+       unsigned int left, right;
+
+#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+
+       switch (effect->type) {
+       case FF_CONSTANT:
+               x = effect->u.ramp.start_level + 0x7f;  /* 0x7f is center */
+               y = effect->u.ramp.end_level + 0x7f;
+               CLAMP(x);
+               CLAMP(y);
+               report->field[0]->value[0] = 0x51;
+               report->field[0]->value[1] = 0x08;
+               report->field[0]->value[2] = x;
+               report->field[0]->value[3] = y;
+               dbg("(x, y)=(%04x, %04x)", x, y);
+               hid_submit_report(hid, report, USB_DIR_OUT);
+               break;
+
+       case FF_RUMBLE:
+               right = effect->u.rumble.strong_magnitude;
+               left = effect->u.rumble.weak_magnitude;
+               right = right * 0xff / 0xffff;
+               left = left * 0xff / 0xffff;
+               CLAMP(left);
+               CLAMP(right);
+               report->field[0]->value[0] = 0x42;
+               report->field[0]->value[1] = 0x00;
+               report->field[0]->value[2] = left;
+               report->field[0]->value[3] = right;
+               dbg("(left, right)=(%04x, %04x)", left, right);
+               hid_submit_report(hid, report, USB_DIR_OUT);
+               break;
+       }
+       return 0;
+}
+
 int hid_lgff_init(struct hid_device* hid)
 {
-       struct lgff_device *private;
-       struct hid_report* report;
-       struct hid_field* field;
+       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct input_dev *dev = hidinput->input;
+       struct hid_report *report;
+       struct hid_field *field;
+       int error;
+       int i, j;
 
        /* Find the report to use */
-       if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
+       if (list_empty(report_list)) {
                err("No output report found");
                return -1;
        }
+
        /* Check that the report looks ok */
-       report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next;
+       report = list_entry(report_list->next, struct hid_report, list);
        if (!report) {
                err("NULL output report");
                return -1;
        }
+
        field = report->field[0];
        if (!field) {
                err("NULL field");
                return -1;
        }
 
-       private = kzalloc(sizeof(struct lgff_device), GFP_KERNEL);
-       if (!private)
-               return -1;
-       hid->ff_private = private;
-
-       /* Input init */
-       hid_lgff_input_init(hid);
-
-
-       private->constant = hid_lgff_duplicate_report(report);
-       if (!private->constant) {
-               kfree(private);
-               return -1;
-       }
-       private->constant->field[0]->value[0] = 0x51;
-       private->constant->field[0]->value[1] = 0x08;
-       private->constant->field[0]->value[2] = 0x7f;
-       private->constant->field[0]->value[3] = 0x7f;
-
-       private->rumble = hid_lgff_duplicate_report(report);
-       if (!private->rumble) {
-               hid_lgff_delete_report(private->constant);
-               kfree(private);
-               return -1;
-       }
-       private->rumble->field[0]->value[0] = 0x42;
-
-
-       private->condition = hid_lgff_duplicate_report(report);
-       if (!private->condition) {
-               hid_lgff_delete_report(private->rumble);
-               hid_lgff_delete_report(private->constant);
-               kfree(private);
-               return -1;
-       }
-
-       private->hid = hid;
-
-       spin_lock_init(&private->lock);
-       init_timer(&private->timer);
-       private->timer.data = (unsigned long)private;
-       private->timer.function = hid_lgff_timer;
-
-       /* Event and exit callbacks */
-       hid->ff_exit = hid_lgff_exit;
-       hid->ff_event = hid_lgff_event;
-
-       /* Start the update task */
-       private->timer.expires = RUN_AT(PERIOD);
-       add_timer(&private->timer);  /*TODO: only run the timer when at least
-                                      one effect is playing */
-
-       printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
-
-       return 0;
-}
-
-static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report)
-{
-       struct hid_report* ret;
-
-       ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
-       if (!ret)
-               return NULL;
-       *ret = *report;
-
-       ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL);
-       if (!ret->field[0]) {
-               kfree(ret);
-               return NULL;
-       }
-       *ret->field[0] = *report->field[0];
-
-       ret->field[0]->value = kzalloc(sizeof(s32[8]), GFP_KERNEL);
-       if (!ret->field[0]->value) {
-               kfree(ret->field[0]);
-               kfree(ret);
-               return NULL;
-       }
-
-       return ret;
-}
-
-static void hid_lgff_delete_report(struct hid_report* report)
-{
-       if (report) {
-               kfree(report->field[0]->value);
-               kfree(report->field[0]);
-               kfree(report);
-       }
-}
-
-static void hid_lgff_input_init(struct hid_device* hid)
-{
-       struct device_type* dev = devices;
-       signed short* ff;
-       u16 idVendor = le16_to_cpu(hid->dev->descriptor.idVendor);
-       u16 idProduct = le16_to_cpu(hid->dev->descriptor.idProduct);
-       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
-       struct input_dev *input_dev = hidinput->input;
-
-       while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct))
-               dev++;
-
-       for (ff = dev->ff; *ff >= 0; ff++)
-               set_bit(*ff, input_dev->ffbit);
-
-       input_dev->upload_effect = hid_lgff_upload_effect;
-       input_dev->flush = hid_lgff_flush;
-
-       set_bit(EV_FF, input_dev->evbit);
-       input_dev->ff_effects_max = LGFF_EFFECTS;
-}
-
-static void hid_lgff_exit(struct hid_device* hid)
-{
-       struct lgff_device *lgff = hid->ff_private;
-
-       set_bit(DEVICE_CLOSING, lgff->flags);
-       del_timer_sync(&lgff->timer);
-
-       hid_lgff_delete_report(lgff->condition);
-       hid_lgff_delete_report(lgff->rumble);
-       hid_lgff_delete_report(lgff->constant);
-
-       kfree(lgff);
-}
-
-static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
-                         unsigned int type, unsigned int code, int value)
-{
-       struct lgff_device *lgff = hid->ff_private;
-       struct lgff_effect *effect = lgff->effects + code;
-       unsigned long flags;
-
-       if (type != EV_FF)                     return -EINVAL;
-       if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES;
-       if (value < 0)                         return -EINVAL;
-
-       spin_lock_irqsave(&lgff->lock, flags);
-
-       if (value > 0) {
-               if (test_bit(EFFECT_STARTED, effect->flags)) {
-                       spin_unlock_irqrestore(&lgff->lock, flags);
-                       return -EBUSY;
-               }
-               if (test_bit(EFFECT_PLAYING, effect->flags)) {
-                       spin_unlock_irqrestore(&lgff->lock, flags);
-                       return -EBUSY;
-               }
-
-               effect->count = value;
-
-               if (effect->effect.replay.delay) {
-                       set_bit(EFFECT_STARTED, effect->flags);
-               } else {
-                       set_bit(EFFECT_PLAYING, effect->flags);
-               }
-               effect->started_at = jiffies;
-       }
-       else { /* value == 0 */
-               clear_bit(EFFECT_STARTED, effect->flags);
-               clear_bit(EFFECT_PLAYING, effect->flags);
-       }
-
-       spin_unlock_irqrestore(&lgff->lock, flags);
-
-       return 0;
-
-}
-
-/* Erase all effects this process owns */
-static int hid_lgff_flush(struct input_dev *dev, struct file *file)
-{
-       struct hid_device *hid = dev->private;
-       struct lgff_device *lgff = hid->ff_private;
-       int i;
-
-       for (i=0; i<dev->ff_effects_max; ++i) {
-
-               /*NOTE: no need to lock here. The only times EFFECT_USED is
-                 modified is when effects are uploaded or when an effect is
-                 erased. But a process cannot close its dev/input/eventX fd
-                 and perform ioctls on the same fd all at the same time */
-               if ( current->pid == lgff->effects[i].owner
-                    && test_bit(EFFECT_USED, lgff->effects[i].flags)) {
-
-                       if (hid_lgff_erase(dev, i))
-                               warn("erase effect %d failed", i);
-               }
-
-       }
-
-       return 0;
-}
-
-static int hid_lgff_erase(struct input_dev *dev, int id)
-{
-       struct hid_device *hid = dev->private;
-       struct lgff_device *lgff = hid->ff_private;
-       unsigned long flags;
-
-       if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES;
-
-       spin_lock_irqsave(&lgff->lock, flags);
-       lgff->effects[id].flags[0] = 0;
-       spin_unlock_irqrestore(&lgff->lock, flags);
-
-       return 0;
-}
-
-static int hid_lgff_upload_effect(struct input_dev* input,
-                                 struct ff_effect* effect)
-{
-       struct hid_device *hid = input->private;
-       struct lgff_device *lgff = hid->ff_private;
-       struct lgff_effect new;
-       int id;
-       unsigned long flags;
-
-       dbg("ioctl rumble");
-
-       if (!test_bit(effect->type, input->ffbit)) return -EINVAL;
-
-       spin_lock_irqsave(&lgff->lock, flags);
-
-       if (effect->id == -1) {
-               int i;
-
-               for (i=0; i<LGFF_EFFECTS && test_bit(EFFECT_USED, lgff->effects[i].flags); ++i);
-               if (i >= LGFF_EFFECTS) {
-                       spin_unlock_irqrestore(&lgff->lock, flags);
-                       return -ENOSPC;
+       for (i = 0; i < ARRAY_SIZE(devices); i++) {
+               if (dev->id.vendor == devices[i].idVendor &&
+                   dev->id.product == devices[i].idProduct) {
+                       for (j = 0; devices[i].ff[j] >= 0; j++)
+                               set_bit(devices[i].ff[j], dev->ffbit);
+                       break;
                }
-
-               effect->id = i;
-               lgff->effects[i].owner = current->pid;
-               lgff->effects[i].flags[0] = 0;
-               set_bit(EFFECT_USED, lgff->effects[i].flags);
        }
-       else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) {
-               spin_unlock_irqrestore(&lgff->lock, flags);
-               return -EACCES;
-       }
-
-       id = effect->id;
-       new = lgff->effects[id];
-
-       new.effect = *effect;
-
-       if (test_bit(EFFECT_STARTED, lgff->effects[id].flags)
-           || test_bit(EFFECT_STARTED, lgff->effects[id].flags)) {
-
-               /* Changing replay parameters is not allowed (for the time
-                  being) */
-               if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay
-                   || new.effect.replay.length != lgff->effects[id].effect.replay.length) {
-                       spin_unlock_irqrestore(&lgff->lock, flags);
-                       return -ENOSYS;
-               }
 
-               lgff->effects[id] = new;
+       error = input_ff_create_memless(dev, NULL, hid_lgff_play);
+       if (error)
+               return error;
 
-       } else {
-               lgff->effects[id] = new;
-       }
+       printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
 
-       spin_unlock_irqrestore(&lgff->lock, flags);
        return 0;
 }
-
-static void hid_lgff_timer(unsigned long timer_data)
-{
-       struct lgff_device *lgff = (struct lgff_device*)timer_data;
-       struct hid_device *hid = lgff->hid;
-       unsigned long flags;
-       int x = 0x7f, y = 0x7f;   // Coordinates of constant effects
-       unsigned int left = 0, right = 0;   // Rumbling
-       int i;
-
-       spin_lock_irqsave(&lgff->lock, flags);
-
-       for (i=0; i<LGFF_EFFECTS; ++i) {
-               struct lgff_effect* effect = lgff->effects +i;
-
-               if (test_bit(EFFECT_PLAYING, effect->flags)) {
-
-                       switch (effect->effect.type) {
-                       case FF_CONSTANT: {
-                               //TODO: handle envelopes
-                               int degrees = effect->effect.direction * 360 >> 16;
-                               x += fixp_mult(fixp_sin(degrees),
-                                              fixp_new16(effect->effect.u.constant.level));
-                               y += fixp_mult(-fixp_cos(degrees),
-                                              fixp_new16(effect->effect.u.constant.level));
-                       }       break;
-                       case FF_RUMBLE:
-                               right += effect->effect.u.rumble.strong_magnitude;
-                               left += effect->effect.u.rumble.weak_magnitude;
-                               break;
-                       };
-
-                       /* One run of the effect is finished playing */
-                       if (time_after(jiffies,
-                                       effect->started_at
-                                       + effect->effect.replay.delay*HZ/1000
-                                       + effect->effect.replay.length*HZ/1000)) {
-                               dbg("Finished playing once %d", i);
-                               if (--effect->count <= 0) {
-                                       dbg("Stopped %d", i);
-                                       clear_bit(EFFECT_PLAYING, effect->flags);
-                               }
-                               else {
-                                       dbg("Start again %d", i);
-                                       if (effect->effect.replay.length != 0) {
-                                               clear_bit(EFFECT_PLAYING, effect->flags);
-                                               set_bit(EFFECT_STARTED, effect->flags);
-                                       }
-                                       effect->started_at = jiffies;
-                               }
-                       }
-
-               } else if (test_bit(EFFECT_STARTED, lgff->effects[i].flags)) {
-                       /* Check if we should start playing the effect */
-                       if (time_after(jiffies,
-                                       lgff->effects[i].started_at
-                                       + lgff->effects[i].effect.replay.delay*HZ/1000)) {
-                               dbg("Now playing %d", i);
-                               clear_bit(EFFECT_STARTED, lgff->effects[i].flags);
-                               set_bit(EFFECT_PLAYING, lgff->effects[i].flags);
-                       }
-               }
-       }
-
-#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
-
-       // Clamp values
-       CLAMP(x);
-       CLAMP(y);
-       CLAMP(left);
-       CLAMP(right);
-
-#undef CLAMP
-
-       if (x != lgff->constant->field[0]->value[2]
-           || y != lgff->constant->field[0]->value[3]) {
-               lgff->constant->field[0]->value[2] = x;
-               lgff->constant->field[0]->value[3] = y;
-               dbg("(x,y)=(%04x, %04x)", x, y);
-               hid_submit_report(hid, lgff->constant, USB_DIR_OUT);
-       }
-
-       if (left != lgff->rumble->field[0]->value[2]
-           || right != lgff->rumble->field[0]->value[3]) {
-               lgff->rumble->field[0]->value[2] = left;
-               lgff->rumble->field[0]->value[3] = right;
-               dbg("(left,right)=(%04x, %04x)", left, right);
-               hid_submit_report(hid, lgff->rumble, USB_DIR_OUT);
-       }
-
-       if (!test_bit(DEVICE_CLOSING, lgff->flags)) {
-               lgff->timer.expires = RUN_AT(PERIOD);
-               add_timer(&lgff->timer);
-       }
-
-       spin_unlock_irqrestore(&lgff->lock, flags);
-}
diff --git a/drivers/usb/input/hid-pidff.c b/drivers/usb/input/hid-pidff.c
new file mode 100644 (file)
index 0000000..5420c13
--- /dev/null
@@ -0,0 +1,1330 @@
+/*
+ *  Force feedback driver for USB HID PID compliant devices
+ *
+ *  Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.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
+ */
+
+/* #define DEBUG */
+
+#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg)
+
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+
+#include "hid.h"
+
+#define        PID_EFFECTS_MAX         64
+
+/* Report usage table used to put reports into an array */
+
+#define PID_SET_EFFECT         0
+#define PID_EFFECT_OPERATION   1
+#define PID_DEVICE_GAIN                2
+#define PID_POOL               3
+#define PID_BLOCK_LOAD         4
+#define PID_BLOCK_FREE         5
+#define PID_DEVICE_CONTROL     6
+#define PID_CREATE_NEW_EFFECT  7
+
+#define PID_REQUIRED_REPORTS   7
+
+#define PID_SET_ENVELOPE       8
+#define PID_SET_CONDITION      9
+#define PID_SET_PERIODIC       10
+#define PID_SET_CONSTANT       11
+#define PID_SET_RAMP           12
+static const u8 pidff_reports[] = {
+       0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab,
+       0x5a, 0x5f, 0x6e, 0x73, 0x74
+};
+
+/* device_control is really 0x95, but 0x96 specified as it is the usage of
+the only field in that report */
+
+/* Value usage tables used to put fields and values into arrays */
+
+#define PID_EFFECT_BLOCK_INDEX 0
+
+#define PID_DURATION           1
+#define PID_GAIN               2
+#define PID_TRIGGER_BUTTON     3
+#define PID_TRIGGER_REPEAT_INT 4
+#define PID_DIRECTION_ENABLE   5
+#define PID_START_DELAY                6
+static const u8 pidff_set_effect[] = {
+       0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7
+};
+
+#define PID_ATTACK_LEVEL       1
+#define PID_ATTACK_TIME                2
+#define PID_FADE_LEVEL         3
+#define PID_FADE_TIME          4
+static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e };
+
+#define PID_PARAM_BLOCK_OFFSET 1
+#define PID_CP_OFFSET          2
+#define PID_POS_COEFFICIENT    3
+#define PID_NEG_COEFFICIENT    4
+#define PID_POS_SATURATION     5
+#define PID_NEG_SATURATION     6
+#define PID_DEAD_BAND          7
+static const u8 pidff_set_condition[] = {
+       0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65
+};
+
+#define PID_MAGNITUDE          1
+#define PID_OFFSET             2
+#define PID_PHASE              3
+#define PID_PERIOD             4
+static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 };
+static const u8 pidff_set_constant[] = { 0x22, 0x70 };
+
+#define PID_RAMP_START         1
+#define PID_RAMP_END           2
+static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 };
+
+#define PID_RAM_POOL_AVAILABLE 1
+static const u8 pidff_block_load[] = { 0x22, 0xac };
+
+#define PID_LOOP_COUNT         1
+static const u8 pidff_effect_operation[] = { 0x22, 0x7c };
+
+static const u8 pidff_block_free[] = { 0x22 };
+
+#define PID_DEVICE_GAIN_FIELD  0
+static const u8 pidff_device_gain[] = { 0x7e };
+
+#define PID_RAM_POOL_SIZE      0
+#define PID_SIMULTANEOUS_MAX   1
+#define PID_DEVICE_MANAGED_POOL        2
+static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 };
+
+/* Special field key tables used to put special field keys into arrays */
+
+#define PID_ENABLE_ACTUATORS   0
+#define PID_RESET              1
+static const u8 pidff_device_control[] = { 0x97, 0x9a };
+
+#define PID_CONSTANT   0
+#define PID_RAMP       1
+#define PID_SQUARE     2
+#define PID_SINE       3
+#define PID_TRIANGLE   4
+#define PID_SAW_UP     5
+#define PID_SAW_DOWN   6
+#define PID_SPRING     7
+#define PID_DAMPER     8
+#define PID_INERTIA    9
+#define PID_FRICTION   10
+static const u8 pidff_effect_types[] = {
+       0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34,
+       0x40, 0x41, 0x42, 0x43
+};
+
+#define PID_BLOCK_LOAD_SUCCESS 0
+#define PID_BLOCK_LOAD_FULL    1
+static const u8 pidff_block_load_status[] = { 0x8c, 0x8d };
+
+#define PID_EFFECT_START       0
+#define PID_EFFECT_STOP                1
+static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b };
+
+struct pidff_usage {
+       struct hid_field *field;
+       s32 *value;
+};
+
+struct pidff_device {
+       struct hid_device *hid;
+
+       struct hid_report *reports[sizeof(pidff_reports)];
+
+       struct pidff_usage set_effect[sizeof(pidff_set_effect)];
+       struct pidff_usage set_envelope[sizeof(pidff_set_envelope)];
+       struct pidff_usage set_condition[sizeof(pidff_set_condition)];
+       struct pidff_usage set_periodic[sizeof(pidff_set_periodic)];
+       struct pidff_usage set_constant[sizeof(pidff_set_constant)];
+       struct pidff_usage set_ramp[sizeof(pidff_set_ramp)];
+
+       struct pidff_usage device_gain[sizeof(pidff_device_gain)];
+       struct pidff_usage block_load[sizeof(pidff_block_load)];
+       struct pidff_usage pool[sizeof(pidff_pool)];
+       struct pidff_usage effect_operation[sizeof(pidff_effect_operation)];
+       struct pidff_usage block_free[sizeof(pidff_block_free)];
+
+       /* Special field is a field that is not composed of
+          usage<->value pairs that pidff_usage values are */
+
+       /* Special field in create_new_effect */
+       struct hid_field *create_new_effect_type;
+
+       /* Special fields in set_effect */
+       struct hid_field *set_effect_type;
+       struct hid_field *effect_direction;
+
+       /* Special field in device_control */
+       struct hid_field *device_control;
+
+       /* Special field in block_load */
+       struct hid_field *block_load_status;
+
+       /* Special field in effect_operation */
+       struct hid_field *effect_operation_status;
+
+       int control_id[sizeof(pidff_device_control)];
+       int type_id[sizeof(pidff_effect_types)];
+       int status_id[sizeof(pidff_block_load_status)];
+       int operation_id[sizeof(pidff_effect_operation_status)];
+
+       int pid_id[PID_EFFECTS_MAX];
+};
+
+/*
+ * Scale an unsigned value with range 0..max for the given field
+ */
+static int pidff_rescale(int i, int max, struct hid_field *field)
+{
+       return i * (field->logical_maximum - field->logical_minimum) / max +
+           field->logical_minimum;
+}
+
+/*
+ * Scale a signed value in range -0x8000..0x7fff for the given field
+ */
+static int pidff_rescale_signed(int i, struct hid_field *field)
+{
+       return i == 0 ? 0 : i >
+           0 ? i * field->logical_maximum / 0x7fff : i *
+           field->logical_minimum / -0x8000;
+}
+
+static void pidff_set(struct pidff_usage *usage, u16 value)
+{
+       usage->value[0] = pidff_rescale(value, 0xffff, usage->field);
+       debug("calculated from %d to %d", value, usage->value[0]);
+}
+
+static void pidff_set_signed(struct pidff_usage *usage, s16 value)
+{
+       if (usage->field->logical_minimum < 0)
+               usage->value[0] = pidff_rescale_signed(value, usage->field);
+       else {
+               if (value < 0)
+                       usage->value[0] =
+                           pidff_rescale(-value, 0x8000, usage->field);
+               else
+                       usage->value[0] =
+                           pidff_rescale(value, 0x7fff, usage->field);
+       }
+       debug("calculated from %d to %d", value, usage->value[0]);
+}
+
+/*
+ * Send envelope report to the device
+ */
+static void pidff_set_envelope_report(struct pidff_device *pidff,
+                                     struct ff_envelope *envelope)
+{
+       pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] =
+           pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+
+       pidff->set_envelope[PID_ATTACK_LEVEL].value[0] =
+           pidff_rescale(envelope->attack_level >
+                         0x7fff ? 0x7fff : envelope->attack_level, 0x7fff,
+                         pidff->set_envelope[PID_ATTACK_LEVEL].field);
+       pidff->set_envelope[PID_FADE_LEVEL].value[0] =
+           pidff_rescale(envelope->fade_level >
+                         0x7fff ? 0x7fff : envelope->fade_level, 0x7fff,
+                         pidff->set_envelope[PID_FADE_LEVEL].field);
+
+       pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length;
+       pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length;
+
+       debug("attack %u => %d", envelope->attack_level,
+             pidff->set_envelope[PID_ATTACK_LEVEL].value[0]);
+
+       hid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE],
+                         USB_DIR_OUT);
+}
+
+/*
+ * Test if the new envelope differs from old one
+ */
+static int pidff_needs_set_envelope(struct ff_envelope *envelope,
+                                   struct ff_envelope *old)
+{
+       return envelope->attack_level != old->attack_level ||
+              envelope->fade_level != old->fade_level ||
+              envelope->attack_length != old->attack_length ||
+              envelope->fade_length != old->fade_length;
+}
+
+/*
+ * Send constant force report to the device
+ */
+static void pidff_set_constant_force_report(struct pidff_device *pidff,
+                                           struct ff_effect *effect)
+{
+       pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] =
+               pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+       pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE],
+                        effect->u.constant.level);
+
+       hid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT],
+                         USB_DIR_OUT);
+}
+
+/*
+ * Test if the constant parameters have changed between effects
+ */
+static int pidff_needs_set_constant(struct ff_effect *effect,
+                                   struct ff_effect *old)
+{
+       return effect->u.constant.level != old->u.constant.level;
+}
+
+/*
+ * Send set effect report to the device
+ */
+static void pidff_set_effect_report(struct pidff_device *pidff,
+                                   struct ff_effect *effect)
+{
+       pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] =
+               pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+       pidff->set_effect_type->value[0] =
+               pidff->create_new_effect_type->value[0];
+       pidff->set_effect[PID_DURATION].value[0] = effect->replay.length;
+       pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button;
+       pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] =
+               effect->trigger.interval;
+       pidff->set_effect[PID_GAIN].value[0] =
+               pidff->set_effect[PID_GAIN].field->logical_maximum;
+       pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
+       pidff->effect_direction->value[0] =
+               pidff_rescale(effect->direction, 0xffff,
+                               pidff->effect_direction);
+       pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay;
+
+       hid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT],
+                         USB_DIR_OUT);
+}
+
+/*
+ * Test if the values used in set_effect have changed
+ */
+static int pidff_needs_set_effect(struct ff_effect *effect,
+                                 struct ff_effect *old)
+{
+       return effect->replay.length != old->replay.length ||
+              effect->trigger.interval != old->trigger.interval ||
+              effect->trigger.button != old->trigger.button ||
+              effect->direction != old->direction ||
+              effect->replay.delay != old->replay.delay;
+}
+
+/*
+ * Send periodic effect report to the device
+ */
+static void pidff_set_periodic_report(struct pidff_device *pidff,
+                                     struct ff_effect *effect)
+{
+       pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] =
+               pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+       pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE],
+                        effect->u.periodic.magnitude);
+       pidff_set_signed(&pidff->set_periodic[PID_OFFSET],
+                        effect->u.periodic.offset);
+       pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase);
+       pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period;
+
+       hid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC],
+                         USB_DIR_OUT);
+
+}
+
+/*
+ * Test if periodic effect parameters have changed
+ */
+static int pidff_needs_set_periodic(struct ff_effect *effect,
+                                   struct ff_effect *old)
+{
+       return effect->u.periodic.magnitude != old->u.periodic.magnitude ||
+              effect->u.periodic.offset != old->u.periodic.offset ||
+              effect->u.periodic.phase != old->u.periodic.phase ||
+              effect->u.periodic.period != old->u.periodic.period;
+}
+
+/*
+ * Send condition effect reports to the device
+ */
+static void pidff_set_condition_report(struct pidff_device *pidff,
+                                      struct ff_effect *effect)
+{
+       int i;
+
+       pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] =
+               pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+
+       for (i = 0; i < 2; i++) {
+               pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i;
+               pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET],
+                                effect->u.condition[i].center);
+               pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT],
+                                effect->u.condition[i].right_coeff);
+               pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT],
+                                effect->u.condition[i].left_coeff);
+               pidff_set(&pidff->set_condition[PID_POS_SATURATION],
+                         effect->u.condition[i].right_saturation);
+               pidff_set(&pidff->set_condition[PID_NEG_SATURATION],
+                         effect->u.condition[i].left_saturation);
+               pidff_set(&pidff->set_condition[PID_DEAD_BAND],
+                         effect->u.condition[i].deadband);
+               hid_wait_io(pidff->hid);
+               hid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION],
+                                 USB_DIR_OUT);
+       }
+}
+
+/*
+ * Test if condition effect parameters have changed
+ */
+static int pidff_needs_set_condition(struct ff_effect *effect,
+                                    struct ff_effect *old)
+{
+       int i;
+       int ret = 0;
+
+       for (i = 0; i < 2; i++) {
+               struct ff_condition_effect *cond = &effect->u.condition[i];
+               struct ff_condition_effect *old_cond = &old->u.condition[i];
+
+               ret |= cond->center != old_cond->center ||
+                      cond->right_coeff != old_cond->right_coeff ||
+                      cond->left_coeff != old_cond->left_coeff ||
+                      cond->right_saturation != old_cond->right_saturation ||
+                      cond->left_saturation != old_cond->left_saturation ||
+                      cond->deadband != old_cond->deadband;
+       }
+
+       return ret;
+}
+
+/*
+ * Send ramp force report to the device
+ */
+static void pidff_set_ramp_force_report(struct pidff_device *pidff,
+                                       struct ff_effect *effect)
+{
+       pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] =
+               pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+       pidff_set_signed(&pidff->set_ramp[PID_RAMP_START],
+                        effect->u.ramp.start_level);
+       pidff_set_signed(&pidff->set_ramp[PID_RAMP_END],
+                        effect->u.ramp.end_level);
+       hid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP],
+                         USB_DIR_OUT);
+}
+
+/*
+ * Test if ramp force parameters have changed
+ */
+static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old)
+{
+       return effect->u.ramp.start_level != old->u.ramp.start_level ||
+              effect->u.ramp.end_level != old->u.ramp.end_level;
+}
+
+/*
+ * Send a request for effect upload to the device
+ *
+ * Returns 0 if device reported success, -ENOSPC if the device reported memory
+ * is full. Upon unknown response the function will retry for 60 times, if
+ * still unsuccessful -EIO is returned.
+ */
+static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
+{
+       int j;
+
+       pidff->create_new_effect_type->value[0] = efnum;
+       hid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT],
+                         USB_DIR_OUT);
+       debug("create_new_effect sent, type: %d", efnum);
+
+       pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0;
+       pidff->block_load_status->value[0] = 0;
+       hid_wait_io(pidff->hid);
+
+       for (j = 0; j < 60; j++) {
+               debug("pid_block_load requested");
+               hid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD],
+                                 USB_DIR_IN);
+               hid_wait_io(pidff->hid);
+               if (pidff->block_load_status->value[0] ==
+                   pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) {
+                       debug("device reported free memory: %d bytes",
+                             pidff->block_load[PID_RAM_POOL_AVAILABLE].value ?
+                               pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
+                       return 0;
+               }
+               if (pidff->block_load_status->value[0] ==
+                   pidff->status_id[PID_BLOCK_LOAD_FULL]) {
+                       debug("not enough memory free: %d bytes",
+                             pidff->block_load[PID_RAM_POOL_AVAILABLE].value ?
+                               pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
+                       return -ENOSPC;
+               }
+       }
+       printk(KERN_ERR "hid-pidff: pid_block_load failed 60 times\n");
+       return -EIO;
+}
+
+/*
+ * Play the effect with PID id n times
+ */
+static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
+{
+       pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
+
+       if (n == 0) {
+               pidff->effect_operation_status->value[0] =
+                       pidff->operation_id[PID_EFFECT_STOP];
+       } else {
+               pidff->effect_operation_status->value[0] =
+                       pidff->operation_id[PID_EFFECT_START];
+               pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
+       }
+
+       hid_wait_io(pidff->hid);
+       hid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
+                         USB_DIR_OUT);
+}
+
+/**
+ * Play the effect with effect id @effect_id for @value times
+ */
+static int pidff_playback(struct input_dev *dev, int effect_id, int value)
+{
+       struct pidff_device *pidff = dev->ff->private;
+
+       pidff_playback_pid(pidff, pidff->pid_id[effect_id], value);
+
+       return 0;
+}
+
+/*
+ * Erase effect with PID id
+ */
+static void pidff_erase_pid(struct pidff_device *pidff, int pid_id)
+{
+       pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
+       hid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE],
+                         USB_DIR_OUT);
+}
+
+/*
+ * Stop and erase effect with effect_id
+ */
+static int pidff_erase_effect(struct input_dev *dev, int effect_id)
+{
+       struct pidff_device *pidff = dev->ff->private;
+       int pid_id = pidff->pid_id[effect_id];
+
+       debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]);
+       pidff_playback_pid(pidff, pid_id, 0);
+       pidff_erase_pid(pidff, pid_id);
+
+       return 0;
+}
+
+/*
+ * Effect upload handler
+ */
+static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
+                              struct ff_effect *old)
+{
+       struct pidff_device *pidff = dev->ff->private;
+       int type_id;
+       int error;
+
+       switch (effect->type) {
+       case FF_CONSTANT:
+               if (!old) {
+                       error = pidff_request_effect_upload(pidff,
+                                       pidff->type_id[PID_CONSTANT]);
+                       if (error)
+                               return error;
+               }
+               if (!old || pidff_needs_set_effect(effect, old))
+                       pidff_set_effect_report(pidff, effect);
+               if (!old || pidff_needs_set_constant(effect, old))
+                       pidff_set_constant_force_report(pidff, effect);
+               if (!old ||
+                   pidff_needs_set_envelope(&effect->u.constant.envelope,
+                                       &old->u.constant.envelope))
+                       pidff_set_envelope_report(pidff,
+                                       &effect->u.constant.envelope);
+               break;
+
+       case FF_PERIODIC:
+               if (!old) {
+                       switch (effect->u.periodic.waveform) {
+                       case FF_SQUARE:
+                               type_id = PID_SQUARE;
+                               break;
+                       case FF_TRIANGLE:
+                               type_id = PID_TRIANGLE;
+                               break;
+                       case FF_SINE:
+                               type_id = PID_SINE;
+                               break;
+                       case FF_SAW_UP:
+                               type_id = PID_SAW_UP;
+                               break;
+                       case FF_SAW_DOWN:
+                               type_id = PID_SAW_DOWN;
+                               break;
+                       default:
+                               printk(KERN_ERR
+                                      "hid-pidff: invalid waveform\n");
+                               return -EINVAL;
+                       }
+
+                       error = pidff_request_effect_upload(pidff,
+                                       pidff->type_id[type_id]);
+                       if (error)
+                               return error;
+               }
+               if (!old || pidff_needs_set_effect(effect, old))
+                       pidff_set_effect_report(pidff, effect);
+               if (!old || pidff_needs_set_periodic(effect, old))
+                       pidff_set_periodic_report(pidff, effect);
+               if (!old ||
+                   pidff_needs_set_envelope(&effect->u.periodic.envelope,
+                                       &old->u.periodic.envelope))
+                       pidff_set_envelope_report(pidff,
+                                       &effect->u.periodic.envelope);
+               break;
+
+       case FF_RAMP:
+               if (!old) {
+                       error = pidff_request_effect_upload(pidff,
+                                       pidff->type_id[PID_RAMP]);
+                       if (error)
+                               return error;
+               }
+               if (!old || pidff_needs_set_effect(effect, old))
+                       pidff_set_effect_report(pidff, effect);
+               if (!old || pidff_needs_set_ramp(effect, old))
+                       pidff_set_ramp_force_report(pidff, effect);
+               if (!old ||
+                   pidff_needs_set_envelope(&effect->u.ramp.envelope,
+                                       &old->u.ramp.envelope))
+                       pidff_set_envelope_report(pidff,
+                                       &effect->u.ramp.envelope);
+               break;
+
+       case FF_SPRING:
+               if (!old) {
+                       error = pidff_request_effect_upload(pidff,
+                                       pidff->type_id[PID_SPRING]);
+                       if (error)
+                               return error;
+               }
+               if (!old || pidff_needs_set_effect(effect, old))
+                       pidff_set_effect_report(pidff, effect);
+               if (!old || pidff_needs_set_condition(effect, old))
+                       pidff_set_condition_report(pidff, effect);
+               break;
+
+       case FF_FRICTION:
+               if (!old) {
+                       error = pidff_request_effect_upload(pidff,
+                                       pidff->type_id[PID_FRICTION]);
+                       if (error)
+                               return error;
+               }
+               if (!old || pidff_needs_set_effect(effect, old))
+                       pidff_set_effect_report(pidff, effect);
+               if (!old || pidff_needs_set_condition(effect, old))
+                       pidff_set_condition_report(pidff, effect);
+               break;
+
+       case FF_DAMPER:
+               if (!old) {
+                       error = pidff_request_effect_upload(pidff,
+                                       pidff->type_id[PID_DAMPER]);
+                       if (error)
+                               return error;
+               }
+               if (!old || pidff_needs_set_effect(effect, old))
+                       pidff_set_effect_report(pidff, effect);
+               if (!old || pidff_needs_set_condition(effect, old))
+                       pidff_set_condition_report(pidff, effect);
+               break;
+
+       case FF_INERTIA:
+               if (!old) {
+                       error = pidff_request_effect_upload(pidff,
+                                       pidff->type_id[PID_INERTIA]);
+                       if (error)
+                               return error;
+               }
+               if (!old || pidff_needs_set_effect(effect, old))
+                       pidff_set_effect_report(pidff, effect);
+               if (!old || pidff_needs_set_condition(effect, old))
+                       pidff_set_condition_report(pidff, effect);
+               break;
+
+       default:
+               printk(KERN_ERR "hid-pidff: invalid type\n");
+               return -EINVAL;
+       }
+
+       if (!old)
+               pidff->pid_id[effect->id] =
+                   pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+
+       debug("uploaded");
+
+       return 0;
+}
+
+/*
+ * set_gain() handler
+ */
+static void pidff_set_gain(struct input_dev *dev, u16 gain)
+{
+       struct pidff_device *pidff = dev->ff->private;
+
+       pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain);
+       hid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
+                         USB_DIR_OUT);
+}
+
+static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
+{
+       struct hid_field *field =
+               pidff->block_load[PID_EFFECT_BLOCK_INDEX].field;
+
+       if (!magnitude) {
+               pidff_playback_pid(pidff, field->logical_minimum, 0);
+               return;
+       }
+
+       pidff_playback_pid(pidff, field->logical_minimum, 1);
+
+       pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] =
+               pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum;
+       pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING];
+       pidff->set_effect[PID_DURATION].value[0] = 0;
+       pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0;
+       pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0;
+       pidff_set(&pidff->set_effect[PID_GAIN], magnitude);
+       pidff->set_effect[PID_START_DELAY].value[0] = 0;
+
+       hid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT],
+                         USB_DIR_OUT);
+}
+
+/*
+ * pidff_set_autocenter() handler
+ */
+static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+       struct pidff_device *pidff = dev->ff->private;
+
+       pidff_autocenter(pidff, magnitude);
+}
+
+/*
+ * Find fields from a report and fill a pidff_usage
+ */
+static int pidff_find_fields(struct pidff_usage *usage, const u8 *table,
+                            struct hid_report *report, int count, int strict)
+{
+       int i, j, k, found;
+
+       for (k = 0; k < count; k++) {
+               found = 0;
+               for (i = 0; i < report->maxfield; i++) {
+                       if (report->field[i]->maxusage !=
+                           report->field[i]->report_count) {
+                               debug("maxusage and report_count do not match, "
+                                     "skipping");
+                               continue;
+                       }
+                       for (j = 0; j < report->field[i]->maxusage; j++) {
+                               if (report->field[i]->usage[j].hid ==
+                                   (HID_UP_PID | table[k])) {
+                                       debug("found %d at %d->%d", k, i, j);
+                                       usage[k].field = report->field[i];
+                                       usage[k].value =
+                                               &report->field[i]->value[j];
+                                       found = 1;
+                                       break;
+                               }
+                       }
+                       if (found)
+                               break;
+               }
+               if (!found && strict) {
+                       debug("failed to locate %d", k);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Return index into pidff_reports for the given usage
+ */
+static int pidff_check_usage(int usage)
+{
+       int i;
+
+       for (i = 0; i < sizeof(pidff_reports); i++)
+               if (usage == (HID_UP_PID | pidff_reports[i]))
+                       return i;
+
+       return -1;
+}
+
+/*
+ * Find the reports and fill pidff->reports[]
+ * report_type specifies either OUTPUT or FEATURE reports
+ */
+static void pidff_find_reports(struct hid_device *hid, int report_type,
+                              struct pidff_device *pidff)
+{
+       struct hid_report *report;
+       int i, ret;
+
+       list_for_each_entry(report,
+                           &hid->report_enum[report_type].report_list, list) {
+               if (report->maxfield < 1)
+                       continue;
+               ret = pidff_check_usage(report->field[0]->logical);
+               if (ret != -1) {
+                       debug("found usage 0x%02x from field->logical",
+                             pidff_reports[ret]);
+                       pidff->reports[ret] = report;
+                       continue;
+               }
+
+               /*
+                * Sometimes logical collections are stacked to indicate
+                * different usages for the report and the field, in which
+                * case we want the usage of the parent. However, Linux HID
+                * implementation hides this fact, so we have to dig it up
+                * ourselves
+                */
+               i = report->field[0]->usage[0].collection_index;
+               if (i <= 0 ||
+                   hid->collection[i - 1].type != HID_COLLECTION_LOGICAL)
+                       continue;
+               ret = pidff_check_usage(hid->collection[i - 1].usage);
+               if (ret != -1 && !pidff->reports[ret]) {
+                       debug("found usage 0x%02x from collection array",
+                             pidff_reports[ret]);
+                       pidff->reports[ret] = report;
+               }
+       }
+}
+
+/*
+ * Test if the required reports have been found
+ */
+static int pidff_reports_ok(struct pidff_device *pidff)
+{
+       int i;
+
+       for (i = 0; i <= PID_REQUIRED_REPORTS; i++) {
+               if (!pidff->reports[i]) {
+                       debug("%d missing", i);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Find a field with a specific usage within a report
+ */
+static struct hid_field *pidff_find_special_field(struct hid_report *report,
+                                                 int usage, int enforce_min)
+{
+       int i;
+
+       for (i = 0; i < report->maxfield; i++) {
+               if (report->field[i]->logical == (HID_UP_PID | usage) &&
+                   report->field[i]->report_count > 0) {
+                       if (!enforce_min ||
+                           report->field[i]->logical_minimum == 1)
+                               return report->field[i];
+                       else {
+                               printk(KERN_ERR "hid-pidff: logical_minimum "
+                                       "is not 1 as it should be\n");
+                               return NULL;
+                       }
+               }
+       }
+       return NULL;
+}
+
+/*
+ * Fill a pidff->*_id struct table
+ */
+static int pidff_find_special_keys(int *keys, struct hid_field *fld,
+                                  const u8 *usagetable, int count)
+{
+
+       int i, j;
+       int found = 0;
+
+       for (i = 0; i < count; i++) {
+               for (j = 0; j < fld->maxusage; j++) {
+                       if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) {
+                               keys[i] = j + 1;
+                               found++;
+                               break;
+                       }
+               }
+       }
+       return found;
+}
+
+#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \
+       pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \
+               sizeof(pidff_ ## name))
+
+/*
+ * Find and check the special fields
+ */
+static int pidff_find_special_fields(struct pidff_device *pidff)
+{
+       debug("finding special fields");
+
+       pidff->create_new_effect_type =
+               pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT],
+                                        0x25, 1);
+       pidff->set_effect_type =
+               pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
+                                        0x25, 1);
+       pidff->effect_direction =
+               pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
+                                        0x57, 0);
+       pidff->device_control =
+               pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL],
+                                        0x96, 1);
+       pidff->block_load_status =
+               pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD],
+                                        0x8b, 1);
+       pidff->effect_operation_status =
+               pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION],
+                                        0x78, 1);
+
+       debug("search done");
+
+       if (!pidff->create_new_effect_type || !pidff->set_effect_type) {
+               printk(KERN_ERR "hid-pidff: effect lists not found\n");
+               return -1;
+       }
+
+       if (!pidff->effect_direction) {
+               printk(KERN_ERR "hid-pidff: direction field not found\n");
+               return -1;
+       }
+
+       if (!pidff->device_control) {
+               printk(KERN_ERR "hid-pidff: device control field not found\n");
+               return -1;
+       }
+
+       if (!pidff->block_load_status) {
+               printk(KERN_ERR
+                      "hid-pidff: block load status field not found\n");
+               return -1;
+       }
+
+       if (!pidff->effect_operation_status) {
+               printk(KERN_ERR
+                      "hid-pidff: effect operation field not found\n");
+               return -1;
+       }
+
+       pidff_find_special_keys(pidff->control_id, pidff->device_control,
+                               pidff_device_control,
+                               sizeof(pidff_device_control));
+
+       PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control);
+
+       if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type,
+                                    effect_types)) {
+               printk(KERN_ERR "hid-pidff: no effect types found\n");
+               return -1;
+       }
+
+       if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status,
+                                   block_load_status) !=
+                       sizeof(pidff_block_load_status)) {
+               printk(KERN_ERR
+                      "hidpidff: block load status identifiers not found\n");
+               return -1;
+       }
+
+       if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status,
+                                   effect_operation_status) !=
+                       sizeof(pidff_effect_operation_status)) {
+               printk(KERN_ERR
+                      "hidpidff: effect operation identifiers not found\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Find the implemented effect types
+ */
+static int pidff_find_effects(struct pidff_device *pidff,
+                             struct input_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < sizeof(pidff_effect_types); i++) {
+               int pidff_type = pidff->type_id[i];
+               if (pidff->set_effect_type->usage[pidff_type].hid !=
+                   pidff->create_new_effect_type->usage[pidff_type].hid) {
+                       printk(KERN_ERR "hid-pidff: "
+                              "effect type number %d is invalid\n", i);
+                       return -1;
+               }
+       }
+
+       if (pidff->type_id[PID_CONSTANT])
+               set_bit(FF_CONSTANT, dev->ffbit);
+       if (pidff->type_id[PID_RAMP])
+               set_bit(FF_RAMP, dev->ffbit);
+       if (pidff->type_id[PID_SQUARE]) {
+               set_bit(FF_SQUARE, dev->ffbit);
+               set_bit(FF_PERIODIC, dev->ffbit);
+       }
+       if (pidff->type_id[PID_SINE]) {
+               set_bit(FF_SINE, dev->ffbit);
+               set_bit(FF_PERIODIC, dev->ffbit);
+       }
+       if (pidff->type_id[PID_TRIANGLE]) {
+               set_bit(FF_TRIANGLE, dev->ffbit);
+               set_bit(FF_PERIODIC, dev->ffbit);
+       }
+       if (pidff->type_id[PID_SAW_UP]) {
+               set_bit(FF_SAW_UP, dev->ffbit);
+               set_bit(FF_PERIODIC, dev->ffbit);
+       }
+       if (pidff->type_id[PID_SAW_DOWN]) {
+               set_bit(FF_SAW_DOWN, dev->ffbit);
+               set_bit(FF_PERIODIC, dev->ffbit);
+       }
+       if (pidff->type_id[PID_SPRING])
+               set_bit(FF_SPRING, dev->ffbit);
+       if (pidff->type_id[PID_DAMPER])
+               set_bit(FF_DAMPER, dev->ffbit);
+       if (pidff->type_id[PID_INERTIA])
+               set_bit(FF_INERTIA, dev->ffbit);
+       if (pidff->type_id[PID_FRICTION])
+               set_bit(FF_FRICTION, dev->ffbit);
+
+       return 0;
+
+}
+
+#define PIDFF_FIND_FIELDS(name, report, strict) \
+       pidff_find_fields(pidff->name, pidff_ ## name, \
+               pidff->reports[report], \
+               sizeof(pidff_ ## name), strict)
+
+/*
+ * Fill and check the pidff_usages
+ */
+static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
+{
+       int envelope_ok = 0;
+
+       if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) {
+               printk(KERN_ERR
+                      "hid-pidff: unknown set_effect report layout\n");
+               return -ENODEV;
+       }
+
+       PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0);
+       if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) {
+               printk(KERN_ERR
+                      "hid-pidff: unknown pid_block_load report layout\n");
+               return -ENODEV;
+       }
+
+       if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) {
+               printk(KERN_ERR
+                      "hid-pidff: unknown effect_operation report layout\n");
+               return -ENODEV;
+       }
+
+       if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) {
+               printk(KERN_ERR
+                      "hid-pidff: unknown pid_block_free report layout\n");
+               return -ENODEV;
+       }
+
+       if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1))
+               envelope_ok = 1;
+
+       if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev))
+               return -ENODEV;
+
+       if (!envelope_ok) {
+               if (test_and_clear_bit(FF_CONSTANT, dev->ffbit))
+                       printk(KERN_WARNING "hid-pidff: "
+                              "has constant effect but no envelope\n");
+               if (test_and_clear_bit(FF_RAMP, dev->ffbit))
+                       printk(KERN_WARNING "hid-pidff: "
+                               "has ramp effect but no envelope\n");
+
+               if (test_and_clear_bit(FF_PERIODIC, dev->ffbit))
+                       printk(KERN_WARNING "hid-pidff: "
+                               "has periodic effect but no envelope\n");
+       }
+
+       if (test_bit(FF_CONSTANT, dev->ffbit) &&
+           PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) {
+               printk(KERN_WARNING
+                      "hid-pidff: unknown constant effect layout\n");
+               clear_bit(FF_CONSTANT, dev->ffbit);
+       }
+
+       if (test_bit(FF_RAMP, dev->ffbit) &&
+           PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) {
+               printk(KERN_WARNING "hid-pidff: unknown ramp effect layout\n");
+               clear_bit(FF_RAMP, dev->ffbit);
+       }
+
+       if ((test_bit(FF_SPRING, dev->ffbit) ||
+            test_bit(FF_DAMPER, dev->ffbit) ||
+            test_bit(FF_FRICTION, dev->ffbit) ||
+            test_bit(FF_INERTIA, dev->ffbit)) &&
+           PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) {
+               printk(KERN_WARNING
+                      "hid-pidff: unknown condition effect layout\n");
+               clear_bit(FF_SPRING, dev->ffbit);
+               clear_bit(FF_DAMPER, dev->ffbit);
+               clear_bit(FF_FRICTION, dev->ffbit);
+               clear_bit(FF_INERTIA, dev->ffbit);
+       }
+
+       if (test_bit(FF_PERIODIC, dev->ffbit) &&
+           PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) {
+               printk(KERN_WARNING
+                      "hid-pidff: unknown periodic effect layout\n");
+               clear_bit(FF_PERIODIC, dev->ffbit);
+       }
+
+       PIDFF_FIND_FIELDS(pool, PID_POOL, 0);
+
+       if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1))
+               set_bit(FF_GAIN, dev->ffbit);
+
+       return 0;
+}
+
+/*
+ * Reset the device
+ */
+static void pidff_reset(struct pidff_device *pidff)
+{
+       struct hid_device *hid = pidff->hid;
+       int i = 0;
+
+       pidff->device_control->value[0] = pidff->control_id[PID_RESET];
+       /* We reset twice as sometimes hid_wait_io isn't waiting long enough */
+       hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
+       hid_wait_io(hid);
+       hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
+       hid_wait_io(hid);
+
+       pidff->device_control->value[0] =
+               pidff->control_id[PID_ENABLE_ACTUATORS];
+       hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
+       hid_wait_io(hid);
+
+       /* pool report is sometimes messed up, refetch it */
+       hid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN);
+       hid_wait_io(hid);
+
+       if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
+               int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0];
+               while (sim_effects < 2) {
+                       if (i++ > 20) {
+                               printk(KERN_WARNING "hid-pidff: device reports "
+                                      "%d simultaneous effects\n",
+                                      sim_effects);
+                               break;
+                       }
+                       debug("pid_pool requested again");
+                       hid_submit_report(hid, pidff->reports[PID_POOL],
+                                         USB_DIR_IN);
+                       hid_wait_io(hid);
+               }
+       }
+}
+
+/*
+ * Test if autocenter modification is using the supported method
+ */
+static int pidff_check_autocenter(struct pidff_device *pidff,
+                                 struct input_dev *dev)
+{
+       int error;
+
+       /*
+        * Let's find out if autocenter modification is supported
+        * Specification doesn't specify anything, so we request an
+        * effect upload and cancel it immediately. If the approved
+        * effect id was one above the minimum, then we assume the first
+        * effect id is a built-in spring type effect used for autocenter
+        */
+
+       error = pidff_request_effect_upload(pidff, 1);
+       if (error) {
+               printk(KERN_ERR "hid-pidff: upload request failed\n");
+               return error;
+       }
+
+       if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] ==
+           pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) {
+               pidff_autocenter(pidff, 0xffff);
+               set_bit(FF_AUTOCENTER, dev->ffbit);
+       } else {
+               printk(KERN_NOTICE "hid-pidff: "
+                      "device has unknown autocenter control method\n");
+       }
+
+       pidff_erase_pid(pidff,
+                       pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]);
+
+       return 0;
+
+}
+
+/*
+ * Check if the device is PID and initialize it
+ */
+int hid_pidff_init(struct hid_device *hid)
+{
+       struct pidff_device *pidff;
+       struct hid_input *hidinput = list_entry(hid->inputs.next,
+                                               struct hid_input, list);
+       struct input_dev *dev = hidinput->input;
+       struct ff_device *ff;
+       int max_effects;
+       int error;
+
+       debug("starting pid init");
+
+       if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
+               debug("not a PID device, no output report");
+               return -ENODEV;
+       }
+
+       pidff = kzalloc(sizeof(*pidff), GFP_KERNEL);
+       if (!pidff)
+               return -ENOMEM;
+
+       pidff->hid = hid;
+
+       pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff);
+       pidff_find_reports(hid, HID_FEATURE_REPORT, pidff);
+
+       if (!pidff_reports_ok(pidff)) {
+               debug("reports not ok, aborting");
+               error = -ENODEV;
+               goto fail;
+       }
+
+       error = pidff_init_fields(pidff, dev);
+       if (error)
+               goto fail;
+
+       pidff_reset(pidff);
+
+       if (test_bit(FF_GAIN, dev->ffbit)) {
+               pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff);
+               hid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
+                                 USB_DIR_OUT);
+       }
+
+       error = pidff_check_autocenter(pidff, dev);
+       if (error)
+               goto fail;
+
+       max_effects =
+           pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum -
+           pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum +
+           1;
+       debug("max effects is %d", max_effects);
+
+       if (max_effects > PID_EFFECTS_MAX)
+               max_effects = PID_EFFECTS_MAX;
+
+       if (pidff->pool[PID_SIMULTANEOUS_MAX].value)
+               debug("max simultaneous effects is %d",
+                     pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
+
+       if (pidff->pool[PID_RAM_POOL_SIZE].value)
+               debug("device memory size is %d bytes",
+                     pidff->pool[PID_RAM_POOL_SIZE].value[0]);
+
+       if (pidff->pool[PID_DEVICE_MANAGED_POOL].value &&
+           pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) {
+               printk(KERN_NOTICE "hid-pidff: "
+                      "device does not support device managed pool\n");
+               goto fail;
+       }
+
+       error = input_ff_create(dev, max_effects);
+       if (error)
+               goto fail;
+
+       ff = dev->ff;
+       ff->private = pidff;
+       ff->upload = pidff_upload_effect;
+       ff->erase = pidff_erase_effect;
+       ff->set_gain = pidff_set_gain;
+       ff->set_autocenter = pidff_set_autocenter;
+       ff->playback = pidff_playback;
+
+       printk(KERN_INFO "Force feedback for USB HID PID devices by "
+              "Anssi Hannula <anssi.hannula@gmail.com>\n");
+
+       return 0;
+
+ fail:
+       kfree(pidff);
+       return error;
+}
index 534425c69c0a502b1a7f86be0811446147c0afd7..2d5be4c318ac2a566e99e89865391a2140a7b2eb 100644 (file)
  */
 
 #include <linux/input.h>
-#include <linux/sched.h>
 
 #undef DEBUG
 #include <linux/usb.h>
 
-#include <linux/circ_buf.h>
-
 #include "hid.h"
-#include "fixp-arith.h"
 
 /* Usages for thrustmaster devices I know about */
 #define THRUSTMASTER_USAGE_RUMBLE_LR   (HID_UP_GENDESK | 0xbb)
-#define DELAY_CALC(t,delay)            ((t) + (delay)*HZ/1000)
-
-/* Effect status */
-#define EFFECT_STARTED 0       /* Effect is going to play after some time */
-#define EFFECT_PLAYING 1       /* Effect is playing */
-#define EFFECT_USED    2
-
-/* For tmff_device::flags */
-#define DEVICE_CLOSING 0       /* The driver is being unitialised */
-
-/* Check that the current process can access an effect */
-#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
-        || effect.owner == current->pid)
-
-#define TMFF_CHECK_ID(id)      ((id) >= 0 && (id) < TMFF_EFFECTS)
 
-#define TMFF_CHECK_OWNERSHIP(i, l) \
-        (test_bit(EFFECT_USED, l->effects[i].flags) \
-        && CHECK_OWNERSHIP(l->effects[i]))
-
-#define TMFF_EFFECTS 8
-
-struct tmff_effect {
-       pid_t owner;
-
-       struct ff_effect effect;
-
-       unsigned long flags[1];
-       unsigned int count;             /* Number of times left to play */
-
-       unsigned long play_at;          /* When the effect starts to play */
-       unsigned long stop_at;          /* When the effect ends */
-};
 
 struct tmff_device {
-       struct hid_device *hid;
-
        struct hid_report *report;
-
        struct hid_field *rumble;
+};
 
-       unsigned int effects_playing;
-       struct tmff_effect effects[TMFF_EFFECTS];
-       spinlock_t lock;             /* device-level lock. Having locks on
-                                       a per-effect basis could be nice, but
-                                       isn't really necessary */
+/* Changes values from 0 to 0xffff into values from minimum to maximum */
+static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
+{
+       int ret;
 
-       unsigned long flags[1];      /* Contains various information about the
-                                       state of the driver for this device */
+       ret = (in * (maximum - minimum) / 0xffff) + minimum;
+       if (ret < minimum)
+               return minimum;
+       if (ret > maximum)
+               return maximum;
+       return ret;
+}
 
-       struct timer_list timer;
-};
+static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+       struct hid_device *hid = dev->private;
+       struct tmff_device *tmff = data;
+       int left, right;        /* Rumbling */
 
-/* Callbacks */
-static void hid_tmff_exit(struct hid_device *hid);
-static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
-                         unsigned int type, unsigned int code, int value);
-static int hid_tmff_flush(struct input_dev *input, struct file *file);
-static int hid_tmff_upload_effect(struct input_dev *input,
-                                 struct ff_effect *effect);
-static int hid_tmff_erase(struct input_dev *input, int id);
+       left = hid_tmff_scale(effect->u.rumble.weak_magnitude,
+               tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
+       right = hid_tmff_scale(effect->u.rumble.strong_magnitude,
+               tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
 
-/* Local functions */
-static void hid_tmff_recalculate_timer(struct tmff_device *tmff);
-static void hid_tmff_timer(unsigned long timer_data);
+       tmff->rumble->value[0] = left;
+       tmff->rumble->value[1] = right;
+       dbg("(left,right)=(%08x, %08x)", left, right);
+       hid_submit_report(hid, tmff->report, USB_DIR_OUT);
+
+       return 0;
+}
 
 int hid_tmff_init(struct hid_device *hid)
 {
-       struct tmff_device *private;
+       struct tmff_device *tmff;
        struct list_head *pos;
        struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
        struct input_dev *input_dev = hidinput->input;
+       int error;
 
-       private = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
-       if (!private)
+       tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
+       if (!tmff)
                return -ENOMEM;
 
-       hid->ff_private = private;
-
        /* Find the report to use */
        __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
                struct hid_report *report = (struct hid_report *)pos;
@@ -142,18 +110,18 @@ int hid_tmff_init(struct hid_device *hid)
                                                continue;
                                        }
 
-                                       if (private->report && private->report != report) {
+                                       if (tmff->report && tmff->report != report) {
                                                warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
                                                continue;
                                        }
 
-                                       if (private->rumble && private->rumble != field) {
+                                       if (tmff->rumble && tmff->rumble != field) {
                                                warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
                                                continue;
                                        }
 
-                                       private->report = report;
-                                       private->rumble = field;
+                                       tmff->report = report;
+                                       tmff->rumble = field;
 
                                        set_bit(FF_RUMBLE, input_dev->ffbit);
                                        break;
@@ -162,302 +130,17 @@ int hid_tmff_init(struct hid_device *hid)
                                        warn("ignoring unknown output usage %08x", field->usage[0].hid);
                                        continue;
                        }
-
-                       /* Fallthrough to here only when a valid usage is found */
-                       input_dev->upload_effect = hid_tmff_upload_effect;
-                       input_dev->flush = hid_tmff_flush;
-
-                       set_bit(EV_FF, input_dev->evbit);
-                       input_dev->ff_effects_max = TMFF_EFFECTS;
                }
        }
 
-       private->hid = hid;
-
-       spin_lock_init(&private->lock);
-       init_timer(&private->timer);
-       private->timer.data = (unsigned long)private;
-       private->timer.function = hid_tmff_timer;
-
-       /* Event and exit callbacks */
-       hid->ff_exit = hid_tmff_exit;
-       hid->ff_event = hid_tmff_event;
-
-       info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
-
-       return 0;
-}
-
-static void hid_tmff_exit(struct hid_device *hid)
-{
-       struct tmff_device *tmff = hid->ff_private;
-       unsigned long flags;
-
-       spin_lock_irqsave(&tmff->lock, flags);
-
-       set_bit(DEVICE_CLOSING, tmff->flags);
-       del_timer_sync(&tmff->timer);
-
-       spin_unlock_irqrestore(&tmff->lock, flags);
-
-       kfree(tmff);
-}
-
-static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
-                         unsigned int type, unsigned int code, int value)
-{
-       struct tmff_device *tmff = hid->ff_private;
-       struct tmff_effect *effect = &tmff->effects[code];
-       unsigned long flags;
-
-       if (type != EV_FF)
-               return -EINVAL;
-       if (!TMFF_CHECK_ID(code))
-               return -EINVAL;
-       if (!TMFF_CHECK_OWNERSHIP(code, tmff))
-               return -EACCES;
-       if (value < 0)
-               return -EINVAL;
-
-       spin_lock_irqsave(&tmff->lock, flags);
-
-       if (value > 0) {
-               set_bit(EFFECT_STARTED, effect->flags);
-               clear_bit(EFFECT_PLAYING, effect->flags);
-               effect->count = value;
-               effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
-       } else {
-               clear_bit(EFFECT_STARTED, effect->flags);
-               clear_bit(EFFECT_PLAYING, effect->flags);
-       }
-
-       hid_tmff_recalculate_timer(tmff);
-
-       spin_unlock_irqrestore(&tmff->lock, flags);
-
-       return 0;
-
-}
-
-/* Erase all effects this process owns */
-
-static int hid_tmff_flush(struct input_dev *dev, struct file *file)
-{
-       struct hid_device *hid = dev->private;
-       struct tmff_device *tmff = hid->ff_private;
-       int i;
-
-       for (i=0; i<dev->ff_effects_max; ++i)
-
-            /* NOTE: no need to lock here. The only times EFFECT_USED is
-               modified is when effects are uploaded or when an effect is
-               erased. But a process cannot close its dev/input/eventX fd
-               and perform ioctls on the same fd all at the same time */
-
-               if (current->pid == tmff->effects[i].owner
-                    && test_bit(EFFECT_USED, tmff->effects[i].flags))
-                       if (hid_tmff_erase(dev, i))
-                               warn("erase effect %d failed", i);
-
-
-       return 0;
-}
-
-static int hid_tmff_erase(struct input_dev *dev, int id)
-{
-       struct hid_device *hid = dev->private;
-       struct tmff_device *tmff = hid->ff_private;
-       unsigned long flags;
-
-       if (!TMFF_CHECK_ID(id))
-               return -EINVAL;
-       if (!TMFF_CHECK_OWNERSHIP(id, tmff))
-               return -EACCES;
-
-       spin_lock_irqsave(&tmff->lock, flags);
-
-       tmff->effects[id].flags[0] = 0;
-       hid_tmff_recalculate_timer(tmff);
-
-       spin_unlock_irqrestore(&tmff->lock, flags);
-
-       return 0;
-}
-
-static int hid_tmff_upload_effect(struct input_dev *input,
-                                 struct ff_effect *effect)
-{
-       struct hid_device *hid = input->private;
-       struct tmff_device *tmff = hid->ff_private;
-       int id;
-       unsigned long flags;
-
-       if (!test_bit(effect->type, input->ffbit))
-               return -EINVAL;
-       if (effect->id != -1 && !TMFF_CHECK_ID(effect->id))
-               return -EINVAL;
-
-       spin_lock_irqsave(&tmff->lock, flags);
-
-       if (effect->id == -1) {
-               /* Find a free effect */
-               for (id = 0; id < TMFF_EFFECTS && test_bit(EFFECT_USED, tmff->effects[id].flags); ++id);
-
-               if (id >= TMFF_EFFECTS) {
-                       spin_unlock_irqrestore(&tmff->lock, flags);
-                       return -ENOSPC;
-               }
-
-               effect->id = id;
-               tmff->effects[id].owner = current->pid;
-               tmff->effects[id].flags[0] = 0;
-               set_bit(EFFECT_USED, tmff->effects[id].flags);
-
-       } else {
-               /* Re-uploading an owned effect, to change parameters */
-               id = effect->id;
-               clear_bit(EFFECT_PLAYING, tmff->effects[id].flags);
+       error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
+       if (error) {
+               kfree(tmff);
+               return error;
        }
 
-       tmff->effects[id].effect = *effect;
-
-       hid_tmff_recalculate_timer(tmff);
+       info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
 
-       spin_unlock_irqrestore(&tmff->lock, flags);
        return 0;
 }
 
-/* Start the timer for the next start/stop/delay */
-/* Always call this while tmff->lock is locked */
-
-static void hid_tmff_recalculate_timer(struct tmff_device *tmff)
-{
-       int i;
-       int events = 0;
-       unsigned long next_time;