Thread (7 messages) 7 messages, 2 authors, 2010-02-25

Re: [PATCH v2 1/2] Input: TWL4030: add Force Feedback vibra

From: Jari Vanhala <hidden>
Date: 2010-02-25 12:13:35

On Tue, 2010-02-23 at 19:24 +0100, ext Dmitry Torokhov wrote:
On Tue, Feb 23, 2010 at 04:27:58PM +0200, Jari Vanhala wrote:
quoted
+static int vibra_play(struct input_dev *dev, void *data,
+                   struct ff_effect *effect)
+{
+     struct vibra_info *info = data;
+
+     spin_lock(&info->lock);
+     if (!info->workqueue)
+             goto out;
+
+     info->speed = effect->u.rumble.strong_magnitude >> 8;
+     if (!info->speed)
+             info->speed = effect->u.rumble.weak_magnitude >> 9;
+     info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
+     queue_work(info->workqueue, &info->play_work);
What if workqueue was not fast enough and previous queue has not been
scheduled yet? It looks like we need to cancel and reschedule the work,
otherwise our scheduling will be off. Or we don't really care?
queue_work() just return 0 if it was already queued. And it's better to
get latest speed than all fast changes, motor isn't that sensitive.
quoted
+out:
+     spin_unlock(&info->lock);
+     return 0;
+}
+
+/*** Module ***/
+#if CONFIG_PM
+static int twl4030_vibra_suspend(struct device *dev)
+{
+     struct platform_device *pdev = to_platform_device(dev);
+     struct vibra_info *info = platform_get_drvdata(pdev);
+
+     if (info->enabled)
+             vibra_disable(info);
+
What will happen if memoryless core will schedule another play effect
right here? It looks like it will re-enable the device... Need to handle
this somehow. Or we depending on the workqueue thread being frozen?
We are trusting that nothing is happening from userspace anymore (as
it's frozen) and all other parts in kernel is also going down. Probably
if userspace doing something, suspend itself could be canceled (unless
forced) and we would get back to normal state.
quoted
+     return 0;
+}
+
+static int twl4030_vibra_resume(struct device *dev)
+{
Why don't we do vibra_enable() here if it was enabled at suspend time?
Just waiting for the next play do do it for us?
We want to keep vibra's power off when it's not running, so we wait for
next play.
quoted
+     vibra_disable_leds();
+     return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
+                      twl4030_vibra_suspend, twl4030_vibra_resume);
+#endif
+
+static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
+{
+     struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data;
+     struct vibra_info *info;
+     int ret;
+
+     if (!pdata) {
+             dev_dbg(&pdev->dev, "platform_data not available\n");
+             return -EINVAL;
+     }
+
+     info = kzalloc(sizeof(*info), GFP_KERNEL);
+     if (!info)
+             return -ENOMEM;
+
+     info->dev = &pdev->dev;
+     info->enabled = false;
Kzalloc did it for us.
Ok.
quoted
+     info->coexist = pdata->coexist ? true : false;
+
+     platform_set_drvdata(pdev, info);
+
+     info->workqueue = create_singlethread_workqueue("vibra");
+     if (info->workqueue == NULL) {
+             dev_err(&pdev->dev, "couldn't create workqueue\n");
+             ret = -ENOMEM;
+             goto err_kzalloc;
+     }
+     INIT_WORK(&info->play_work, vibra_play_work);
+     spin_lock_init(&info->lock);
+
+     info->input_dev = input_allocate_device();
+     if (info->input_dev == NULL) {
+             dev_err(&pdev->dev, "couldn't allocate input device\n");
+             ret = -ENOMEM;
+             goto err_workq;
+     }
+     input_set_drvdata(info->input_dev, info);
+
+     info->input_dev->name = "twl4030:vibrator";
+     info->input_dev->id.version = 1;
+     info->input_dev->dev.parent = pdev->dev.parent;
+     set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+     ret = input_register_device(info->input_dev);
+     if (ret < 0) {
+             dev_dbg(&pdev->dev, "couldn't register input device\n");
+             goto err_ialloc;
+     }
+
+     ret = input_ff_create_memless(info->input_dev, info, vibra_play);
+     if (ret < 0) {
+             dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
+             goto err_ireg;
+     }
This needs to happen before registering input device.
And I thought I checked that call order was right.. Fixing. Keeping info
as data to play is getting too complicated so I drop it and get it via
input-dev (@play).
quoted
+
+     vibra_disable_leds();
+
+     return 0;
+err_ireg:
+     input_unregister_device(info->input_dev);
+     info->input_dev = NULL;
+err_ialloc:
+     input_free_device(info->input_dev);
+err_workq:
+     destroy_workqueue(info->workqueue);
+err_kzalloc:
+     kfree(info);
+     return ret;
+}
+
+static int __devexit twl4030_vibra_remove(struct platform_device *pdev)
+{
+     struct vibra_info *info = platform_get_drvdata(pdev);
+     struct workqueue_struct *wq;
+
+     spin_lock(&info->lock);
+     wq = info->workqueue;
+     info->workqueue = NULL;
+     spin_unlock(&info->lock);
If you combine this and the next patch then this locking is not needed.
Hum. Unregister seems to call close.. Great.
quoted
+
+     cancel_work_sync(&info->play_work);
+     destroy_workqueue(wq);
+     if (info->enabled)
+             vibra_disable(info);
+     /* this also free ff-memless which (in turn) kfree info */
+     input_unregister_device(info->input_dev);
+
It is a good etiquette to do "platform_set_drvdata(pdev, NULL);" to
avoid leaving dangling pointers behind.
Ok.

	++Jam

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help