Input: use new FF interface in the HID force feedback drivers
authorAnssi Hannula <anssi.hannula@gmail.com>
Wed, 19 Jul 2006 05:40:55 +0000 (01:40 -0400)
committerDmitry Torokhov <dtor@insightbb.com>
Wed, 19 Jul 2006 05:40:55 +0000 (01:40 -0400)
Signed-off-by: Anssi Hannula <anssi.hannula@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/usb/input/Kconfig
drivers/usb/input/hid-core.c
drivers/usb/input/hid-input.c
drivers/usb/input/hid-lgff.c
drivers/usb/input/hid-tmff.c
drivers/usb/input/hid.h

index 7ec22c8..3ca64c3 100644 (file)
@@ -70,6 +70,7 @@ config HID_PID
 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,6 +82,7 @@ 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.
index b9fb968..2f477a8 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;
 
index 37b9ea8..4c62afb 100644 (file)
@@ -729,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 7e264a8..93da222 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 "../../input/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);
-}
index bc6c54c..2d5be4c 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 "../../input/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;
-
-       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);
-}
index 29f1613..6d88c0c 100644 (file)
@@ -449,11 +449,6 @@ struct hid_device {                                                        /* device report descriptor */
        char phys[64];                                                  /* Device physical location */
        char uniq[64];                                                  /* Device unique identifier (serial #) */
 
-       void *ff_private;                                               /* Private data for the force-feedback driver */
-       void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
-       int (*ff_event)(struct hid_device *hid, struct input_dev *input,
-                       unsigned int type, unsigned int code, int value);
-
 #ifdef CONFIG_USB_HIDINPUT_POWERBOOK
        unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
        unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
@@ -540,17 +535,3 @@ static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; }
 static inline int hid_ff_init(struct hid_device *hid) { return -1; }
 #endif
 
-static inline void hid_ff_exit(struct hid_device *hid)
-{
-       if (hid->ff_exit)
-               hid->ff_exit(hid);
-}
-
-static inline int hid_ff_event(struct hid_device *hid, struct input_dev *input,
-                       unsigned int type, unsigned int code, int value)
-{
-       if (hid->ff_event)
-               return hid->ff_event(hid, input, type, code, value);
-       return -ENOSYS;
-}
-