[PATCH v2 4/7] staging: unisys: visorinput: respond to resolution changes on-the-fly

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Tim Sell <Timothy.Sell@xxxxxxxxxx>

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.Sell@xxxxxxxxxx>
Signed-off-by: Benjamin Romer <benjamin.romer@xxxxxxxxxx>

---
v2: the patch was resubmitted.
---
 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 8c22e0f..77e0252 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.5.0

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



[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux