From 096abb260c658b931b807310c60a39f87ba8e05e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Mal=C3=BD?= Date: Sat, 20 Sep 2014 01:45:08 +0200 Subject: [PATCH] API change! - Revised logic of effect state transition handling - Simpler and more flexible plugin<->HW-spec driver interface --- klgd_ff_plugin.c | 260 +++++++++++++++++++++++++++++++++------------ klgd_ff_plugin.h | 30 +++++- klgd_ff_plugin_p.h | 17 +-- klgdff.c | 117 +++++++++++++++++--- 4 files changed, 331 insertions(+), 93 deletions(-) diff --git a/klgd_ff_plugin.c b/klgd_ff_plugin.c index c66560f..a5362a0 100644 --- a/klgd_ff_plugin.c +++ b/klgd_ff_plugin.c @@ -1,10 +1,118 @@ -#include "klgd_ff_plugin.h" #include "klgd_ff_plugin_p.h" #include 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; diff --git a/klgd_ff_plugin.h b/klgd_ff_plugin.h index 0ad1e33..9a58d52 100644 --- a/klgd_ff_plugin.h +++ b/klgd_ff_plugin.h @@ -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)); diff --git a/klgd_ff_plugin_p.h b/klgd_ff_plugin_p.h index 6823991..0dce9a9 100644 --- a/klgd_ff_plugin_p.h +++ b/klgd_ff_plugin_p.h @@ -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); }; diff --git a/klgdff.c b/klgdff.c index b22f708..1987ba2 100644 --- 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; -- 2.43.5