root/drivers/input/input-poller.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Support for polling mode for input devices.
 */

#include <linux/device.h>
#include <linux/export.h>
#include <linux/input.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include "input-poller.h"

struct input_dev_poller {
        void (*poll)(struct input_dev *dev);

        unsigned int poll_interval; /* msec */
        unsigned int poll_interval_max; /* msec */
        unsigned int poll_interval_min; /* msec */

        struct input_dev *input;
        struct delayed_work work;
};

static void input_dev_poller_queue_work(struct input_dev_poller *poller)
{
        unsigned long delay;

        delay = msecs_to_jiffies(poller->poll_interval);
        if (delay >= HZ)
                delay = round_jiffies_relative(delay);

        queue_delayed_work(system_freezable_wq, &poller->work, delay);
}

static void input_dev_poller_work(struct work_struct *work)
{
        struct input_dev_poller *poller =
                container_of(work, struct input_dev_poller, work.work);

        poller->poll(poller->input);
        input_dev_poller_queue_work(poller);
}

void input_dev_poller_finalize(struct input_dev_poller *poller)
{
        if (!poller->poll_interval)
                poller->poll_interval = 500;
        if (!poller->poll_interval_max)
                poller->poll_interval_max = poller->poll_interval;
}

void input_dev_poller_start(struct input_dev_poller *poller)
{
        /* Only start polling if polling is enabled */
        if (poller->poll_interval > 0) {
                poller->poll(poller->input);
                input_dev_poller_queue_work(poller);
        }
}

void input_dev_poller_stop(struct input_dev_poller *poller)
{
        cancel_delayed_work_sync(&poller->work);
}

int input_setup_polling(struct input_dev *dev,
                        void (*poll_fn)(struct input_dev *dev))
{
        struct input_dev_poller *poller;

        poller = kzalloc_obj(*poller);
        if (!poller) {
                /*
                 * We want to show message even though kzalloc() may have
                 * printed backtrace as knowing what instance of input
                 * device we were dealing with is helpful.
                 */
                dev_err(dev->dev.parent ?: &dev->dev,
                        "%s: unable to allocate poller structure\n", __func__);
                return -ENOMEM;
        }

        INIT_DELAYED_WORK(&poller->work, input_dev_poller_work);
        poller->input = dev;
        poller->poll = poll_fn;

        dev->poller = poller;
        return 0;
}
EXPORT_SYMBOL(input_setup_polling);

static bool input_dev_ensure_poller(struct input_dev *dev)
{
        if (!dev->poller) {
                dev_err(dev->dev.parent ?: &dev->dev,
                        "poller structure has not been set up\n");
                return false;
        }

        return true;
}

void input_set_poll_interval(struct input_dev *dev, unsigned int interval)
{
        if (input_dev_ensure_poller(dev))
                dev->poller->poll_interval = interval;
}
EXPORT_SYMBOL(input_set_poll_interval);

void input_set_min_poll_interval(struct input_dev *dev, unsigned int interval)
{
        if (input_dev_ensure_poller(dev))
                dev->poller->poll_interval_min = interval;
}
EXPORT_SYMBOL(input_set_min_poll_interval);

void input_set_max_poll_interval(struct input_dev *dev, unsigned int interval)
{
        if (input_dev_ensure_poller(dev))
                dev->poller->poll_interval_max = interval;
}
EXPORT_SYMBOL(input_set_max_poll_interval);

int input_get_poll_interval(struct input_dev *dev)
{
        if (!dev->poller)
                return -EINVAL;

        return dev->poller->poll_interval;
}
EXPORT_SYMBOL(input_get_poll_interval);

/* SYSFS interface */

static ssize_t input_dev_get_poll_interval(struct device *dev,
                                           struct device_attribute *attr,
                                           char *buf)
{
        struct input_dev *input = to_input_dev(dev);

        return sprintf(buf, "%d\n", input->poller->poll_interval);
}

static ssize_t input_dev_set_poll_interval(struct device *dev,
                                           struct device_attribute *attr,
                                           const char *buf, size_t count)
{
        struct input_dev *input = to_input_dev(dev);
        struct input_dev_poller *poller = input->poller;
        unsigned int interval;
        int err;

        err = kstrtouint(buf, 0, &interval);
        if (err)
                return err;

        if (interval < poller->poll_interval_min)
                return -EINVAL;

        if (interval > poller->poll_interval_max)
                return -EINVAL;

        guard(mutex)(&input->mutex);

        poller->poll_interval = interval;

        if (input_device_enabled(input)) {
                cancel_delayed_work_sync(&poller->work);
                if (poller->poll_interval > 0)
                        input_dev_poller_queue_work(poller);
        }

        return count;
}

static DEVICE_ATTR(poll, 0644,
                   input_dev_get_poll_interval, input_dev_set_poll_interval);

static ssize_t input_dev_get_poll_max(struct device *dev,
                                      struct device_attribute *attr, char *buf)
{
        struct input_dev *input = to_input_dev(dev);

        return sprintf(buf, "%d\n", input->poller->poll_interval_max);
}

static DEVICE_ATTR(max, 0444, input_dev_get_poll_max, NULL);

static ssize_t input_dev_get_poll_min(struct device *dev,
                                     struct device_attribute *attr, char *buf)
{
        struct input_dev *input = to_input_dev(dev);

        return sprintf(buf, "%d\n", input->poller->poll_interval_min);
}

static DEVICE_ATTR(min, 0444, input_dev_get_poll_min, NULL);

static umode_t input_poller_attrs_visible(struct kobject *kobj,
                                          struct attribute *attr, int n)
{
        struct device *dev = kobj_to_dev(kobj);
        struct input_dev *input = to_input_dev(dev);

        return input->poller ? attr->mode : 0;
}

static struct attribute *input_poller_attrs[] = {
        &dev_attr_poll.attr,
        &dev_attr_max.attr,
        &dev_attr_min.attr,
        NULL
};

struct attribute_group input_poller_attribute_group = {
        .is_visible     = input_poller_attrs_visible,
        .attrs          = input_poller_attrs,
};