@@ -46,10 +46,12 @@ static int input_polldev_start_workqueue(void)
return retval;
}
-static void input_polldev_stop_workqueue(void)
+static void input_polldev_stop_workqueue(struct input_polled_dev *dev)
{
mutex_lock(&polldev_mutex);
+ cancel_delayed_work_sync(&dev->work);
+
if (!--polldev_users)
destroy_workqueue(polldev_wq);
@@ -83,6 +85,8 @@ static int input_open_polled_device(struct input_dev *input)
if (dev->open)
dev->open(dev);
+ dev->is_opened = 1;
+
queue_delayed_work(polldev_wq, &dev->work,
msecs_to_jiffies(dev->poll_interval));
@@ -93,13 +97,72 @@ static void input_close_polled_device(struct input_dev *input)
{
struct input_polled_dev *dev = input_get_drvdata(input);
- cancel_delayed_work_sync(&dev->work);
- input_polldev_stop_workqueue();
+ dev->is_opened = 0;
+ input_polldev_stop_workqueue(dev);
if (dev->close)
dev->close(dev);
}
+/* SYSFS interface */
+
+static ssize_t input_polldev_get_interval(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", polldev->poll_interval);
+}
+
+static ssize_t input_polldev_set_interval(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct input_polled_dev *polldev = dev_get_drvdata(dev);
+ unsigned long delay;
+ unsigned long interval;
+
+ if (strict_strtoul(buf, 0, &interval))
+ return -EINVAL;
+
+ if (interval < polldev->poll_interval_min)
+ return -EINVAL;
+
+ if (interval > polldev->poll_interval_max)
+ return -EINVAL;
+
+ mutex_lock(&polldev_mutex);
+
+ polldev->poll_interval = interval;
+
+ if (polldev->is_opened) {
+ if (polldev->poll_interval > 0) {
+ delay = msecs_to_jiffies(polldev->poll_interval);
+ if (delay >= HZ)
+ delay = round_jiffies_relative(delay);
+
+ queue_delayed_work(polldev_wq, &polldev->work, delay);
+ } else {
+ cancel_delayed_work_sync(&polldev->work);
+ }
+ }
+ mutex_unlock(&polldev_mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(interval, S_IRUGO | S_IWUSR, input_polldev_get_interval,
+ input_polldev_set_interval);
+
+static struct attribute *sysfs_attrs[] = {
+ &dev_attr_interval.attr,
+ NULL
+};
+
+static struct attribute_group input_polldev_attribute_group = {
+ .attrs = sysfs_attrs
+};
+
/**
* input_allocate_polled_device - allocated memory polled device
*
@@ -153,15 +216,27 @@ EXPORT_SYMBOL(input_free_polled_device);
int input_register_polled_device(struct input_polled_dev *dev)
{
struct input_dev *input = dev->input;
+ int error;
input_set_drvdata(input, dev);
INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
if (!dev->poll_interval)
dev->poll_interval = 500;
+ if (!dev->poll_interval_max)
+ dev->poll_interval_max = dev->poll_interval;
input->open = input_open_polled_device;
input->close = input_close_polled_device;
- return input_register_device(input);
+ error = input_register_device(input);
+ if (error < 0)
+ goto fail;
+
+ error = sysfs_create_group(&input->dev.kobj,
+ &input_polldev_attribute_group);
+ if (error < 0)
+ input_unregister_device(input);
+fail:
+ return error;
}
EXPORT_SYMBOL(input_register_polled_device);
@@ -177,6 +252,9 @@ EXPORT_SYMBOL(input_register_polled_device);
*/
void input_unregister_polled_device(struct input_polled_dev *dev)
{
+ sysfs_remove_group(&dev->input->dev.kobj,
+ &input_polldev_attribute_group);
+
input_unregister_device(dev->input);
dev->input = NULL;
}
@@ -36,6 +36,9 @@ struct input_polled_dev {
void (*close)(struct input_polled_dev *dev);
void (*poll)(struct input_polled_dev *dev);
unsigned int poll_interval; /* msec */
+ unsigned int poll_interval_max; /* msec */
+ unsigned int poll_interval_min; /* msec */
+ int is_opened;
struct input_dev *input;
struct delayed_work work;