From: Tim Sell <timothy.s...@unisys.com>

Whenever the absolute x,y resolution of the video (and mouse) changes, the
back-end sends an inputaction_set_max_xy message indicating the new
resolution.  This commit adds the infrastructure to detect and correctly
respond to that message.

There were several reasons this wasn't as easy as it sounds:

* input_set_abs_params() is only effective if it is called prior to
  input_register_device().  So we need to free the input_dev and
  re-create it whenever the resolution changes.

* Because freeing the input_dev and re-creating it will take us thru
  visorinput_close() and visorinput_open() if someone in user-land has the
  device open, and because those will end up calling
  visorbus_enable_channel_interrupts() and
  visorbus_disable_channel_interrupts(), we canNOT just free the input_dev
  and re-create it inline within visorinput_channel_interrupt().  We need
  to use a workqueue to do it asynchronously.

Signed-off-by: Tim Sell <timothy.s...@unisys.com>
Signed-off-by: Benjamin Romer <benjamin.ro...@unisys.com>
---
 drivers/staging/unisys/visorinput/visorinput.c | 71 ++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)

diff --git a/drivers/staging/unisys/visorinput/visorinput.c 
b/drivers/staging/unisys/visorinput/visorinput.c
index 3dc1e78..cbca2dc 100644
--- a/drivers/staging/unisys/visorinput/visorinput.c
+++ b/drivers/staging/unisys/visorinput/visorinput.c
@@ -28,6 +28,7 @@
 #include <linux/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/uuid.h>
+#include <linux/workqueue.h>
 
 #include "version.h"
 #include "visorbus.h"
@@ -86,6 +87,11 @@ enum visorinput_device_type {
        visorinput_mouse,
 };
 
+struct change_resolution_work {
+       struct work_struct work;
+       unsigned xres, yres;
+};
+
 /*
  * This is the private data that we store for each device.
  * A pointer to this struct is maintained via
@@ -97,6 +103,8 @@ struct visorinput_devdata {
        struct rw_semaphore lock_visor_dev; /* lock for dev */
        struct input_dev *visorinput_dev;
        bool paused;
+       struct workqueue_struct *wq;
+       struct change_resolution_work change_resolution_work_data;
        unsigned int keycode_table_bytes; /* size of following array */
        /* for keyboard devices: visorkbd_keycode[] + visorkbd_ext_keycode[] */
        unsigned char keycode_table[0];
@@ -384,6 +392,10 @@ static void devdata_release(struct kref *kref)
        struct visorinput_devdata *devdata =
                container_of(kref, struct visorinput_devdata, kref);
        unregister_client_input(devdata->visorinput_dev);
+       if (devdata->wq) {
+               flush_workqueue(devdata->wq);
+               destroy_workqueue(devdata->wq);
+       }
        kfree(devdata);
 }
 
@@ -401,6 +413,51 @@ static void devdata_put(struct visorinput_devdata *devdata)
                kref_put(&devdata->kref, devdata_release);
 }
 
+static void async_change_resolution(struct work_struct *work)
+{
+       struct change_resolution_work *p_change_resolution_work =
+               container_of(work, struct change_resolution_work, work);
+       struct visorinput_devdata *devdata =
+               container_of(p_change_resolution_work,
+                            struct visorinput_devdata,
+                            change_resolution_work_data);
+
+       down_write(&devdata->lock_visor_dev);
+
+       if (devdata->paused) /* don't touch device/channel when paused */
+               goto out_locked;
+       if (!devdata->visorinput_dev)
+               goto out_locked;
+
+       unregister_client_input(devdata->visorinput_dev);
+       /*
+        * input_set_abs_params is only effective prior to
+        * input_register_device().
+        */
+       devdata->visorinput_dev =
+               register_client_mouse(devdata,
+                                     p_change_resolution_work->xres,
+                                     p_change_resolution_work->yres);
+       if (!devdata->visorinput_dev)
+               dev_err(&devdata->dev->device,
+                       "failed create of new mouse input dev for new 
resolution %u,%u\n",
+                       p_change_resolution_work->xres,
+                       p_change_resolution_work->yres);
+
+out_locked:
+       up_write(&devdata->lock_visor_dev);
+       devdata_put(devdata);  /* from schedule_mouse_resolution_change() */
+}
+
+static void schedule_mouse_resolution_change(struct visorinput_devdata 
*devdata,
+                                            unsigned xres, unsigned yres)
+{
+       devdata->change_resolution_work_data.xres = xres;
+       devdata->change_resolution_work_data.yres = yres;
+       devdata_get(devdata);  /* don't go away until work processed */
+       queue_work(devdata->wq, &devdata->change_resolution_work_data.work);
+}
+
 static struct visorinput_devdata *
 devdata_create(struct visor_device *dev, enum visorinput_device_type devtype)
 {
@@ -415,6 +472,9 @@ devdata_create(struct visor_device *dev, enum 
visorinput_device_type devtype)
        if (!devdata)
                return NULL;
        devdata->dev = dev;
+       devdata->wq = alloc_ordered_workqueue("visorinput", 0);
+       INIT_WORK(&devdata->change_resolution_work_data.work,
+                 async_change_resolution);
        init_rwsem(&devdata->lock_visor_dev);
        down_write(&devdata->lock_visor_dev);
 
@@ -678,6 +738,17 @@ visorinput_channel_interrupt(struct visor_device *dev)
                        input_report_rel(visorinput_dev, REL_WHEEL, -1);
                        input_sync(visorinput_dev);
                        break;
+               case inputaction_set_max_xy:
+                       /*
+                        * we can NOT handle this inline, because this may go
+                        * thru a close() path, which will attempt to stop the
+                        * worker thread we are running on, which will end up
+                        * deadlocking
+                        */
+                       schedule_mouse_resolution_change(devdata,
+                                                        r.activity.arg1,
+                                                        r.activity.arg2);
+                       break;
                }
        }
 out_locked:
-- 
2.1.4

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to