]> Devoid-pointer.net GitWeb - KLGD_FF_plugin.git/commitdiff
API change!
authorMichal Malý <madcatxster@devoid-pointer.net>
Fri, 19 Sep 2014 23:45:08 +0000 (01:45 +0200)
committerMichal Malý <madcatxster@devoid-pointer.net>
Fri, 19 Sep 2014 23:45:08 +0000 (01:45 +0200)
- Revised logic of effect state transition handling
- Simpler and more flexible plugin<->HW-spec driver interface

klgd_ff_plugin.c
klgd_ff_plugin.h
klgd_ff_plugin_p.h
klgdff.c

index c66560f32f76c29acaa6a258e893cc83ee058962..a5362a0e414cbc31daecc3fc2e0d1073b6daaa5e 100644 (file)
-#include "klgd_ff_plugin.h"
 #include "klgd_ff_plugin_p.h"
 #include <linux/slab.h>
 
 static bool ffpl_has_gain(const struct ff_effect *eff);
 static bool ffpl_replace_effect(const struct ff_effect *ac_eff, const struct ff_effect *la_eff);
 
+static int ffpl_erase_effect(struct klgd_plugin_private *priv, struct klgd_command_stream *s, struct ffpl_effect *eff)
+{
+       if (eff->uploaded_to_device) {
+               struct input_dev *dev = priv->dev;
+               union ffpl_control_data data;
+               int ret;
+
+               data.effect = eff->active;
+               ret = priv->control(dev, s, FFPL_UPL_TO_EMP, data);
+               if (ret)
+                       return ret;
+       }
+
+       kfree(eff->active);
+       eff->active = NULL;
+       eff->state = FFPL_EMPTY;
+       eff->uploaded_to_device = false;
+       return 0;
+}
+
+static int ffpl_start_effect(struct klgd_plugin_private *priv, struct klgd_command_stream *s, struct ffpl_effect *eff)
+{
+       struct input_dev *dev = priv->dev;
+       union ffpl_control_data data;
+       int ret;
+       enum ffpl_control_command cmd;
+
+       data.effect = eff->active;
+       if (priv->upload_when_started) {
+               if (eff->uploaded_to_device)
+                       cmd = FFPL_UPL_TO_SRT;
+               else
+                       cmd = FFPL_EMP_TO_SRT;
+
+               ret = priv->control(dev, s, cmd, data);
+               if (ret)
+                       return ret;
+       } else {
+               /* This can happen only if device supports "upload and start" */
+               if (eff->state == FFPL_EMPTY)
+                       cmd = FFPL_EMP_TO_SRT;
+               else
+                       cmd = FFPL_UPL_TO_SRT;
+               ret = priv->control(dev, s, cmd, data);
+               if (ret)
+                       return ret;
+       }
+
+       eff->uploaded_to_device = true; /* Needed of devices that support "upload and start" but don't use "upload when started" */
+       eff->state = FFPL_STARTED;
+       return 0;
+}
+
+static int ffpl_stop_effect(struct klgd_plugin_private *priv, struct klgd_command_stream *s, struct ffpl_effect *eff)
+{
+       struct input_dev *dev = priv->dev;
+       union ffpl_control_data data;
+       int ret;
+       enum ffpl_control_command cmd;
+
+       data.effect = eff->active;
+       if (priv->erase_when_stopped || (priv->has_srt_to_emp && eff->change == FFPL_TO_ERASE))
+               cmd = FFPL_SRT_TO_EMP;
+       else
+               cmd = FFPL_SRT_TO_UPL;
+
+       ret = priv->control(dev, s, cmd, data);
+       if (ret)
+               return ret;
+       if (cmd == FFPL_SRT_TO_EMP)
+               eff->uploaded_to_device = false;
+       eff->state = FFPL_UPLOADED;
+       return 0;
+}
+
+static int ffpl_update_effect(struct klgd_plugin_private *priv, struct klgd_command_stream *s, struct ffpl_effect *eff)
+{
+       struct input_dev *dev = priv->dev;
+       union ffpl_control_data data;
+
+       data.effect = eff->active;
+       if (!eff->uploaded_to_device)
+               return ffpl_start_effect(priv, s, eff);
+       return priv->control(dev, s, FFPL_SRT_TO_UDT, data);
+}
+
+static int ffpl_upload_effect(struct klgd_plugin_private *priv, struct klgd_command_stream *s, struct ffpl_effect *eff)
+{
+       struct input_dev *dev = priv->dev;
+       union ffpl_control_data data;
+       int ret;
+
+       if (priv->upload_when_started) {
+               eff->state = FFPL_UPLOADED;
+               goto set;
+       }
+
+       data.effect = eff->latest;
+       ret = priv->control(dev, s, FFPL_EMP_TO_SRT, data);
+       if (ret)
+               return ret;
+       eff->uploaded_to_device = true;
+
+set:
+       eff->state = FFPL_UPLOADED;
+       eff->active = eff->latest;
+       return 0;
+}
+
 /* Destroy request - input device is being destroyed */
 static void ffpl_destroy_rq(struct ff_device *ff)
 {      
@@ -89,7 +197,7 @@ static int ffpl_upload_rq(struct input_dev *dev, struct ff_effect *effect, struc
        return 0;
 }
 
-
+/*FIXME: Rewrite this! */
 static void ffpl_set_gain_rq(struct input_dev *dev, u16 gain)
 {
        struct klgd_plugin *self = dev->ff->private;
@@ -105,7 +213,7 @@ static void ffpl_set_gain_rq(struct input_dev *dev, u16 gain)
                if (ffpl_has_gain(eff->active))
                        eff->change = FFPL_TO_UPDATE;
        }
-       priv->gain = gain;
+       /* priv->gain = gain;*/
 
        klgd_unlock_plugins_sched(self->plugins_lock);
 }
@@ -118,8 +226,8 @@ static void ffpl_deinit(struct klgd_plugin *self)
 static struct klgd_command_stream * ffpl_get_commands(struct klgd_plugin *self, const unsigned long now)
 {
        struct klgd_plugin_private *priv = self->private;
-       struct input_dev *const dev = priv->dev;
        struct klgd_command_stream *s;
+       int ret = 0;
        size_t idx;
 
        s = klgd_alloc_stream();
@@ -138,90 +246,90 @@ static struct klgd_command_stream * ffpl_get_commands(struct klgd_plugin *self,
                if (eff->replace) {
                        switch (eff->state) {
                        case FFPL_STARTED:
-                               klgd_append_cmd(s, priv->stop_effect(dev, eff->active, idx));
+                               ret = ffpl_stop_effect(priv, s, eff);
+                               if (ret)
+                                       break;
+                       case FFPL_UPLOADED:
+                               ret = ffpl_erase_effect(priv, s, eff);
+                               if (ret)
+                                       break;
                        default:
-                               klgd_append_cmd(s, priv->erase_effect(dev, eff->active, idx));
-                               kfree(eff->active);
-                               eff->active = NULL;
                                break;
                        }
                        eff->replace = false;
 
-                       /* The new effect is to be erased anyway */
-                       if (eff->change == FFPL_TO_ERASE) {
-                               eff->change = FFPL_DONT_TOUCH;
+                       /* Even the new effect is about to be erased */
+                       if (eff->change == FFPL_TO_ERASE)
                                continue;
-                       }
                }
 
-               /* Figure out the state change since the last update and build
-                * command stream accordingly */
-               switch (eff->state) {
-               case FFPL_EMPTY:
-                       switch (eff->change) {
-                       case FFPL_TO_UPLOAD:
-                       case FFPL_TO_STOP:
-                               klgd_append_cmd(s, priv->upload_effect(dev, eff->latest, idx));
-                               eff->active = eff->latest;
-                               eff->state = FFPL_UPLOADED;
+               switch (eff->change) {
+               case FFPL_TO_UPLOAD:
+                       switch (eff->state) {
+                       case FFPL_EMPTY:
+                               ret = ffpl_upload_effect(priv, s, eff);
                                break;
-                       case FFPL_TO_START:
-                               klgd_append_cmd(s, priv->upload_effect(dev, eff->latest, idx));
-                               eff->active = eff->latest;
-                               klgd_append_cmd(s, priv->start_effect(dev, eff->active, idx));
-                               eff->state = FFPL_STARTED;
+                       case FFPL_STARTED:
+                               ret = ffpl_stop_effect(priv, s, eff);
                                break;
                        default:
                                break;
                        }
                        break;
-               case FFPL_UPLOADED:
-                       switch (eff->change) {
-                       case FFPL_TO_START:
-                               klgd_append_cmd(s, priv->start_effect(dev, eff->active, idx));
-                               eff->state = FFPL_STARTED;
+               case FFPL_TO_START:
+                       switch (eff->state) {
+                       case FFPL_EMPTY:
+                               if (priv->has_emp_to_srt) {
+                                       ret = ffpl_start_effect(priv, s, eff);
+                                       break;
+                               }
+                               ret = ffpl_upload_effect(priv, s, eff);
+                               if (ret)
+                                       break;
+                               ret = ffpl_start_effect(priv, s, eff);
+                               break;
+                       case FFPL_UPLOADED:
+                               ret = ffpl_start_effect(priv, s, eff);
                                break;
-                       case FFPL_TO_ERASE:
-                               klgd_append_cmd(s, priv->erase_effect(dev, eff->active, idx));
-                               kfree(eff->active);
-                               if (eff->active != eff->latest)
-                                       kfree(eff->latest);
-                               eff->latest = NULL;
-                               eff->active = NULL;
-                               eff->state = FFPL_EMPTY;
+                       default:
+                               break;
+                       }
+                       break;
+               case FFPL_TO_STOP:
+                       switch (eff->state) {
+                       case FFPL_STARTED:
+                               ret = ffpl_stop_effect(priv, s, eff);
                                break;
-                       case FFPL_TO_UPDATE:
-                               klgd_append_cmd(s, priv->upload_effect(dev, eff->latest, idx));
-                               eff->active = eff->latest;
+                       case FFPL_EMPTY:
+                               ret = ffpl_upload_effect(priv, s, eff);
                                break;
                        default:
                                break;
                        }
                        break;
-               case FFPL_STARTED:
-                       switch (eff->change) {
-                       case FFPL_TO_STOP:
-                               klgd_append_cmd(s, priv->stop_effect(dev, eff->active, idx));
-                               eff->state = FFPL_UPLOADED;
+               case FFPL_TO_ERASE:
+                       switch (eff->state) {
+                       case FFPL_UPLOADED:
+                               ret = ffpl_erase_effect(priv, s, eff);
                                break;
-                       case FFPL_TO_ERASE:
-                               klgd_append_cmd(s, priv->stop_effect(dev, eff->active, idx));
-                               klgd_append_cmd(s, priv->erase_effect(dev, eff->active, idx));
-                               kfree(eff->active);
-                               if (eff->active != eff->latest)
-                                       kfree(eff->latest);
-                               eff->latest = NULL;
-                               eff->active = NULL;
-                               eff->state = FFPL_EMPTY;
+                       case FFPL_STARTED:
+                               ret = ffpl_stop_effect(priv, s, eff);
+                               if (ret)
+                                       break;
+                               ret = ffpl_erase_effect(priv, s, eff);
                                break;
-                       case FFPL_TO_UPDATE:
-                               klgd_append_cmd(s, priv->upload_effect(dev, eff->latest, idx));
-                               eff->active = eff->latest;
                        default:
                                break;
                        }
                        break;
+               case FFPL_TO_UPDATE:
+                       ret = ffpl_update_effect(priv, s, eff);
+               default:
+                       pr_debug("Unhandled state\n");
                }
+
+               /* TODO: Handle errors */
+
                eff->change = FFPL_DONT_TOUCH;
        }
 
@@ -283,10 +391,8 @@ static int ffpl_init(struct klgd_plugin *self)
 /* Initialize the plugin */
 int ffpl_init_plugin(struct klgd_plugin **plugin, struct input_dev *dev, const size_t effect_count,
                     const unsigned long supported_effects,
-                    struct klgd_command * (*upload)(struct input_dev *dev, const struct ff_effect *effect, const int id),
-                    struct klgd_command * (*start)(struct input_dev *dev, const struct ff_effect *effect, const int id),
-                    struct klgd_command * (*stop)(struct input_dev *dev, const struct ff_effect *effect, const int id),
-                    struct klgd_command * (*erase)(struct input_dev *dev, const struct ff_effect *effect, const int id))
+                    const unsigned long flags,
+                    int (*control)(struct input_dev *dev, struct klgd_command_stream *s, const enum ffpl_control_command cmd, const union ffpl_control_data data))
 {
        struct klgd_plugin *self;
        struct klgd_plugin_private *priv;
@@ -315,14 +421,30 @@ int ffpl_init_plugin(struct klgd_plugin **plugin, struct input_dev *dev, const s
        priv->supported_effects = supported_effects;
        priv->effect_count = effect_count;
        priv->dev = dev;
-       priv->upload_effect = upload;
-       priv->start_effect = start;
-       priv->stop_effect = stop;
-       priv->erase_effect = erase;
+       priv->control = control;
 
        self->private = priv;
        *plugin = self;
 
+       if (FFPL_HAS_EMP_TO_SRT & flags) {
+               priv->has_emp_to_srt = true;
+               printk("KLGDFF: Using HAS EMP_TO_SRT\n");
+       }
+       if (FFPL_HAS_SRT_TO_EMP & flags) {
+               priv->has_srt_to_emp = true;
+               printk("KLGDFF: Using HAS SRT_TO_EMP\n");
+       }
+       if (FFPL_UPLOAD_WHEN_STARTED & flags) {
+               priv->has_emp_to_srt = true;
+               priv->upload_when_started = true;
+               printk("KLGDFF: Using UPLOAD WHEN STARTED\n");
+       }
+       if (FFPL_ERASE_WHEN_STOPPED & flags) {
+               priv->has_srt_to_emp = true;
+               priv->erase_when_stopped = true;
+               printk("KLGDFF: Using ERASE WHEN STOPPED\n");
+       }
+
        set_bit(FF_GAIN, dev->ffbit);
        for (idx = 0; idx <= (FF_EFFECT_MAX - FF_EFFECT_MIN); idx++) {
                if (test_bit(idx, &priv->supported_effects)) {
@@ -333,7 +455,7 @@ int ffpl_init_plugin(struct klgd_plugin **plugin, struct input_dev *dev, const s
        return 0;
 
 err_out2:
-       kfree(self->private->effects);
+       kfree(priv);
 err_out1:
        kfree(self);
        return ret;
index 0ad1e33e140bf49700bc8d545157ae7929f8c954..9a58d52fd9d02fe2717dc5dc4b339eb5ce789574 100644 (file)
@@ -3,9 +3,31 @@
 
 #define FFPL_EFBIT(x) BIT(x - FF_EFFECT_MIN)
 
+/* Allowed flag bits */
+#define FFPL_HAS_EMP_TO_SRT BIT(0) /* Device supports direct "upload and start" */
+#define FFPL_HAS_SRT_TO_EMP BIT(1) /* Device supports direct "stop and erase" */
+#define FFPL_UPLOAD_WHEN_STARTED BIT(2) /* Upload effects only when they are started - this implies HAS_EMP_TO_SRT */
+#define FFPL_ERASE_WHEN_STOPPED BIT(3) /* Erases effect from device when it is stopped - this implies HAS_SRT_TO_EMP */
+
+enum ffpl_control_command {
+       /* Force feedback state transitions */
+       FFPL_EMP_TO_UPL, /* Upload to empty slot */
+       FFPL_UPL_TO_SRT, /* Start uploaded effect */
+       FFPL_SRT_TO_UPL, /* Stop started effect */
+       FFPL_UPL_TO_EMP, /* Erase uploaded effect */
+       FFPL_SRT_TO_UDT, /* Update started effect */
+       /* Optional force feedback state transitions */
+       FFPL_EMP_TO_SRT, /* Upload and start effect */
+       FFPL_SRT_TO_EMP  /* Stop and erase started effect */
+};
+
+union ffpl_control_data {
+       const struct ff_effect *effect;
+       u16 ac_magnitude;
+       u16 gain;
+};
+
 int ffpl_init_plugin(struct klgd_plugin **plugin, struct input_dev *dev, const size_t effect_count,
                     const unsigned long supported_effects,
-                    struct klgd_command * (*upload)(struct input_dev *dev, const struct ff_effect *effect, const int id),
-                    struct klgd_command * (*play)(struct input_dev *dev, const struct ff_effect *effect, const int id),
-                    struct klgd_command * (*stop)(struct input_dev *dev, const struct ff_effect *effect, const int id),
-                    struct klgd_command * (*erase)(struct input_dev *dev, const struct ff_effect *effect, const int id));
+                    const unsigned long flags,
+                    int (*control)(struct input_dev *dev, struct klgd_command_stream *s, const enum ffpl_control_command cmd, const union ffpl_control_data data));
index 6823991415e93da21453632d5333010043feed52..0dce9a920fbe6adbb131fd6eb5ba4b1ca130b840 100644 (file)
@@ -1,5 +1,6 @@
+#include "klgd_ff_plugin.h"
 
-/* State change flags */
+/* Possible state changes of an effect */
 enum ffpl_st_change {
        FFPL_DONT_TOUCH,  /* Effect has not been changed since last update */
        FFPL_TO_UPLOAD,   /* Effect shall be uploaded to device */
@@ -9,7 +10,7 @@ enum ffpl_st_change {
        FFPL_TO_UPDATE    /* Effect paramaters shall be updated */
 };
 
-/* Status flags */
+/* Possible states of an effect */
 enum ffpl_state {
        FFPL_EMPTY,       /* There is no effect in the slot */
        FFPL_UPLOADED,    /* Effect in the slot is uploaded to device */
@@ -22,16 +23,18 @@ struct ffpl_effect {
        enum ffpl_st_change change;     /* State to which the effect shall be put */
        enum ffpl_state state;          /* State of the active effect */
        bool replace;                   /* Active effect has to be replaced => active effect shall be erased and latest uploaded */
+       bool uploaded_to_device;        /* Effect was physically uploaded to device */
 };
 
 struct klgd_plugin_private {
        struct ffpl_effect *effects;
        unsigned long supported_effects;
        size_t effect_count;
-       u16 gain;
        struct input_dev *dev;
-       struct klgd_command * (*upload_effect)(struct input_dev *dev, const struct ff_effect *effect, const int id);
-       struct klgd_command * (*start_effect)(struct input_dev *dev, const struct ff_effect *effect, const int id);
-       struct klgd_command * (*stop_effect)(struct input_dev *dev, const struct ff_effect *effect, const int id);
-       struct klgd_command * (*erase_effect)(struct input_dev *dev, const struct ff_effect *effect, const int id);
+       /* Optional device capabilities */
+       bool has_emp_to_srt;
+       bool has_srt_to_emp;
+       bool upload_when_started;
+       bool erase_when_stopped;
+       int (*control)(struct input_dev *dev, struct klgd_command_stream *s, const enum ffpl_control_command cmd, const union ffpl_control_data data);
 };
index b22f70848f240048ff49c4cecccd4024e4a0311d..1987ba2ecb3f6cd3a189ef061dee225acfab1612 100644 (file)
--- a/klgdff.c
+++ b/klgdff.c
@@ -13,44 +13,102 @@ static struct input_dev *dev;
 static struct klgd_main klgd;
 static struct klgd_plugin *ff_plugin;
 
-static struct klgd_command * klgdff_erase(struct input_dev *dev, const struct ff_effect *effect, const int id)
+static int klgdff_erase(struct klgd_command_stream *s, const struct ff_effect *effect)
 {
-       char *text = kasprintf(GFP_KERNEL, "Erasing effect, type %d, id %d", effect->type, id);
+       char *text = kasprintf(GFP_KERNEL, "Erasing effect, type %d, id %d", effect->type, effect->id);
        size_t len = strlen(text);
        struct klgd_command *c = klgd_alloc_cmd(len + 1);
+
+       if (!c)
+               return -ENOMEM;
+
+       memcpy(c->bytes, text, len);
+       kfree(text);
+       return klgd_append_cmd(s, c);
+}
+
+static int klgdff_er_stop(struct klgd_command_stream *s, const struct ff_effect *effect)
+{
+       char *text = kasprintf(GFP_KERNEL, "Stopping and erasing effect, type %d, id %d", effect->type, effect->id);
+       size_t len = strlen(text);
+       struct klgd_command *c = klgd_alloc_cmd(len + 1);
+
+       if (!c)
+               return -ENOMEM;
+
+       memcpy(c->bytes, text, len);
+       kfree(text);
+       return klgd_append_cmd(s, c);
+}
+
+static int klgdff_start(struct klgd_command_stream *s, const struct ff_effect *effect)
+{
+       char *text = kasprintf(GFP_KERNEL, "Playing effect, type %d, id %d", effect->type, effect->id);
+       size_t len = strlen(text);
+       struct klgd_command *c = klgd_alloc_cmd(len + 1);
+
+       if (!c)
+               return -ENOMEM;
+
+       memcpy(c->bytes, text, len);
+       kfree(text);
+       return klgd_append_cmd(s, c);
+}
+
+static int klgdff_stop(struct klgd_command_stream *s, const struct ff_effect *effect)
+{
+       char *text = kasprintf(GFP_KERNEL, "Stopping effect, type %d, id %d", effect->type, effect->id);
+       size_t len = strlen(text);
+       struct klgd_command *c = klgd_alloc_cmd(len + 1);
+
+       if (!c)
+               return -ENOMEM;
+
        memcpy(c->bytes, text, len);
        kfree(text);
-       return c;
+       return klgd_append_cmd(s, c);
 }
 
-static struct klgd_command * klgdff_start(struct input_dev *de, const struct ff_effect *effect, const int id)
+static int klgdff_update(struct klgd_command_stream *s, const struct ff_effect *effect)
 {
-       char *text = kasprintf(GFP_KERNEL, "Playing effect, type %d, id %d", effect->type, id);
+       char *text = kasprintf(GFP_KERNEL, "Updating effect, type %d, id %d", effect->type, effect->id);
        size_t len = strlen(text);
        struct klgd_command *c = klgd_alloc_cmd(len + 1);
+
+       if (!c)
+               return -ENOMEM;
+
        memcpy(c->bytes, text, len);
        kfree(text);
-       return c;
+       return klgd_append_cmd(s, c);
 }
 
-static struct klgd_command * klgdff_stop(struct input_dev *dev, const struct ff_effect *effect, const int id)
+static int klgdff_upload(struct klgd_command_stream *s, const struct ff_effect *effect)
 {
-       char *text = kasprintf(GFP_KERNEL, "Stopping effect, type %d, id %d", effect->type, id);
+       char *text = kasprintf(GFP_KERNEL, "Uploading effect, type %d, id %d", effect->type, effect->id);
        size_t len = strlen(text);
        struct klgd_command *c = klgd_alloc_cmd(len + 1);
+
+       if (!c)
+               return -ENOMEM;
+
        memcpy(c->bytes, text, len);
        kfree(text);
-       return c;
+       return klgd_append_cmd(s, c);
 }
 
-static struct klgd_command * klgdff_upload(struct input_dev *dev, const struct ff_effect *effect, const int id)
+static int klgdff_up_start(struct klgd_command_stream *s, const struct ff_effect *effect)
 {
-       char *text = kasprintf(GFP_KERNEL, "Uploading effect, type %d, id %d", effect->type, id);
+       char *text = kasprintf(GFP_KERNEL, "Uploading and starting effect, type %d, id %d", effect->type, effect->id);
        size_t len = strlen(text);
        struct klgd_command *c = klgd_alloc_cmd(len + 1);
+
+       if (!c)
+               return -ENOMEM;
+
        memcpy(c->bytes, text, len);
        kfree(text);
-       return c;
+       return klgd_append_cmd(s, c);
 }
 
 int klgdff_callback(void *data, const struct klgd_command_stream *s)
@@ -66,6 +124,38 @@ int klgdff_callback(void *data, const struct klgd_command_stream *s)
        return 0;
 }
 
+int klgdff_control(struct input_dev *dev, struct klgd_command_stream *s, const enum ffpl_control_command cmd, const union ffpl_control_data data)
+{
+       switch (cmd) {
+       case FFPL_EMP_TO_UPL:
+               return klgdff_upload(s, data.effect);
+               break;
+       case FFPL_UPL_TO_SRT:
+               return klgdff_start(s, data.effect);
+               break;
+       case FFPL_SRT_TO_UPL:
+               return klgdff_stop(s, data.effect);
+               break;
+       case FFPL_UPL_TO_EMP:
+               return klgdff_erase(s, data.effect);
+               break;
+       case FFPL_SRT_TO_UDT:
+               return klgdff_update(s, data.effect);
+               break;
+       /* "Uploadless" commands */
+       case FFPL_EMP_TO_SRT:
+               return klgdff_up_start(s, data.effect);
+               break;
+       case FFPL_SRT_TO_EMP:
+               return klgdff_er_stop(s, data.effect);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 static void __exit klgdff_exit(void)
 {
        input_unregister_device(dev);
@@ -111,7 +201,8 @@ static int __init klgdff_init(void)
        input_set_abs_params(dev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
 
        ret = ffpl_init_plugin(&ff_plugin, dev, EFFECT_COUNT, ffbits,
-                              klgdff_upload, klgdff_start, klgdff_stop, klgdff_erase);
+                              FFPL_ERASE_WHEN_STOPPED,
+                              klgdff_control);
        if (ret) {
                printk(KERN_ERR "KLGDFF: Cannot init plugin\n");
                goto errout_idev;