fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / usb / input / hid-tmff.c
index 023fd5a..ab67331 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"
+#include <linux/hid.h>
+#include "usbhid.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);
+       usbhid_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 = kmalloc(sizeof(struct tmff_device), GFP_KERNEL);
-       if (!private)
+       tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
+       if (!tmff)
                return -ENOMEM;
 
-       memset(private, 0, sizeof(struct tmff_device));
-       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;
@@ -143,18 +111,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;
@@ -163,302 +131,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;
-
-       next_time = 0;  /* Shut up compiler's incorrect warning */
-
-       /* Find the next change in an effect's status */
-       for (i = 0; i < TMFF_EFFECTS; ++i) {
-               struct tmff_effect *effect = &tmff->effects[i];
-               unsigned long play_time;
-
-               if (!test_bit(EFFECT_STARTED, effect->flags))
-                       continue;
-
-               effect->stop_at = DELAY_CALC(effect->play_at, effect->effect.replay.length);
-
-               if (!test_bit(EFFECT_PLAYING, effect->flags))
-                       play_time = effect->play_at;
-               else
-                       play_time = effect->stop_at;
-
-               events++;
-
-               if (time_after(jiffies, play_time))
-                       play_time = jiffies;
-
-               if (events == 1)
-                       next_time = play_time;
-               else {
-                       if (time_after(next_time, play_time))
-                               next_time = play_time;
-               }
-       }
-
-       if (!events && tmff->effects_playing) {
-               /* Treat all effects turning off as an event */
-               events = 1;
-               next_time = jiffies;
-       }
-
-       if (!events) {
-               /* No events, no time, no need for a timer. */
-               del_timer_sync(&tmff->timer);
-               return;
-       }
-
-       mod_timer(&tmff->timer, next_time);
-}
-
-/* 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;
-
-       ret = (in * (maximum - minimum) / 0xffff) + minimum;
-       if (ret < minimum)
-               return minimum;
-       if (ret > maximum)
-               return maximum;
-       return ret;
-}
-
-static void hid_tmff_timer(unsigned long timer_data)
-{
-       struct tmff_device *tmff = (struct tmff_device *) timer_data;
-       struct hid_device *hid = tmff->hid;
-       unsigned long flags;
-       int left = 0, right = 0;        /* Rumbling */
-       int i;
-
-       spin_lock_irqsave(&tmff->lock, flags);
-
-       tmff->effects_playing = 0;
-
-       for (i = 0; i < TMFF_EFFECTS; ++i) {
-               struct tmff_effect *effect = &tmff->effects[i];
-
-               if (!test_bit(EFFECT_STARTED, effect->flags))
-                       continue;
-
-               if (!time_after(jiffies, effect->play_at))
-                       continue;
-
-               if (time_after(jiffies, effect->stop_at)) {
-
-                       dbg("Finished playing once %d", i);
-                       clear_bit(EFFECT_PLAYING, effect->flags);
-
-                       if (--effect->count <= 0) {
-                               dbg("Stopped %d", i);
-                               clear_bit(EFFECT_STARTED, effect->flags);
-                               continue;
-                       } else {
-                               dbg("Start again %d", i);
-                               effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
-                               continue;
-                       }
-               }
-
-               ++tmff->effects_playing;
-
-               set_bit(EFFECT_PLAYING, effect->flags);
-
-               switch (effect->effect.type) {
-                       case FF_RUMBLE:
-                               right += effect->effect.u.rumble.strong_magnitude;
-                               left += effect->effect.u.rumble.weak_magnitude;
-                               break;
-                       default:
-                               BUG();
-                               break;
-               }
-       }
-
-       left = hid_tmff_scale(left, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
-       right = hid_tmff_scale(right, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
-
-       if (left != tmff->rumble->value[0] || right != tmff->rumble->value[1]) {
-               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);
-       }
-
-       if (!test_bit(DEVICE_CLOSING, tmff->flags))
-               hid_tmff_recalculate_timer(tmff);
-
-       spin_unlock_irqrestore(&tmff->lock, flags);
-}