linux/drivers/input/mouse/gpio_mouse.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Driver for simulating a mouse on GPIO lines.
   4 *
   5 * Copyright (C) 2007 Atmel Corporation
   6 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/platform_device.h>
  11#include <linux/input-polldev.h>
  12#include <linux/gpio/consumer.h>
  13#include <linux/property.h>
  14#include <linux/of.h>
  15
  16/**
  17 * struct gpio_mouse
  18 * @scan_ms: the scan interval in milliseconds.
  19 * @up: GPIO line for up value.
  20 * @down: GPIO line for down value.
  21 * @left: GPIO line for left value.
  22 * @right: GPIO line for right value.
  23 * @bleft: GPIO line for left button.
  24 * @bmiddle: GPIO line for middle button.
  25 * @bright: GPIO line for right button.
  26 *
  27 * This struct must be added to the platform_device in the board code.
  28 * It is used by the gpio_mouse driver to setup GPIO lines and to
  29 * calculate mouse movement.
  30 */
  31struct gpio_mouse {
  32        u32 scan_ms;
  33        struct gpio_desc *up;
  34        struct gpio_desc *down;
  35        struct gpio_desc *left;
  36        struct gpio_desc *right;
  37        struct gpio_desc *bleft;
  38        struct gpio_desc *bmiddle;
  39        struct gpio_desc *bright;
  40};
  41
  42/*
  43 * Timer function which is run every scan_ms ms when the device is opened.
  44 * The dev input variable is set to the the input_dev pointer.
  45 */
  46static void gpio_mouse_scan(struct input_polled_dev *dev)
  47{
  48        struct gpio_mouse *gpio = dev->private;
  49        struct input_dev *input = dev->input;
  50        int x, y;
  51
  52        if (gpio->bleft)
  53                input_report_key(input, BTN_LEFT,
  54                                 gpiod_get_value(gpio->bleft));
  55        if (gpio->bmiddle)
  56                input_report_key(input, BTN_MIDDLE,
  57                                 gpiod_get_value(gpio->bmiddle));
  58        if (gpio->bright)
  59                input_report_key(input, BTN_RIGHT,
  60                                 gpiod_get_value(gpio->bright));
  61
  62        x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left);
  63        y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up);
  64
  65        input_report_rel(input, REL_X, x);
  66        input_report_rel(input, REL_Y, y);
  67        input_sync(input);
  68}
  69
  70static int gpio_mouse_probe(struct platform_device *pdev)
  71{
  72        struct device *dev = &pdev->dev;
  73        struct gpio_mouse *gmouse;
  74        struct input_polled_dev *input_poll;
  75        struct input_dev *input;
  76        int ret;
  77
  78        gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL);
  79        if (!gmouse)
  80                return -ENOMEM;
  81
  82        /* Assign some default scanning time */
  83        ret = device_property_read_u32(dev, "scan-interval-ms",
  84                                       &gmouse->scan_ms);
  85        if (ret || gmouse->scan_ms == 0) {
  86                dev_warn(dev, "invalid scan time, set to 50 ms\n");
  87                gmouse->scan_ms = 50;
  88        }
  89
  90        gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN);
  91        if (IS_ERR(gmouse->up))
  92                return PTR_ERR(gmouse->up);
  93        gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN);
  94        if (IS_ERR(gmouse->down))
  95                return PTR_ERR(gmouse->down);
  96        gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN);
  97        if (IS_ERR(gmouse->left))
  98                return PTR_ERR(gmouse->left);
  99        gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN);
 100        if (IS_ERR(gmouse->right))
 101                return PTR_ERR(gmouse->right);
 102
 103        gmouse->bleft = devm_gpiod_get_optional(dev, "button-left", GPIOD_IN);
 104        if (IS_ERR(gmouse->bleft))
 105                return PTR_ERR(gmouse->bleft);
 106        gmouse->bmiddle = devm_gpiod_get_optional(dev, "button-middle",
 107                                                  GPIOD_IN);
 108        if (IS_ERR(gmouse->bmiddle))
 109                return PTR_ERR(gmouse->bmiddle);
 110        gmouse->bright = devm_gpiod_get_optional(dev, "button-right",
 111                                                 GPIOD_IN);
 112        if (IS_ERR(gmouse->bright))
 113                return PTR_ERR(gmouse->bright);
 114
 115        input_poll = devm_input_allocate_polled_device(dev);
 116        if (!input_poll) {
 117                dev_err(dev, "not enough memory for input device\n");
 118                return -ENOMEM;
 119        }
 120
 121        platform_set_drvdata(pdev, input_poll);
 122
 123        /* set input-polldev handlers */
 124        input_poll->private = gmouse;
 125        input_poll->poll = gpio_mouse_scan;
 126        input_poll->poll_interval = gmouse->scan_ms;
 127
 128        input = input_poll->input;
 129        input->name = pdev->name;
 130        input->id.bustype = BUS_HOST;
 131        input->dev.parent = &pdev->dev;
 132
 133        input_set_capability(input, EV_REL, REL_X);
 134        input_set_capability(input, EV_REL, REL_Y);
 135        if (gmouse->bleft)
 136                input_set_capability(input, EV_KEY, BTN_LEFT);
 137        if (gmouse->bmiddle)
 138                input_set_capability(input, EV_KEY, BTN_MIDDLE);
 139        if (gmouse->bright)
 140                input_set_capability(input, EV_KEY, BTN_RIGHT);
 141
 142        ret = input_register_polled_device(input_poll);
 143        if (ret) {
 144                dev_err(dev, "could not register input device\n");
 145                return ret;
 146        }
 147
 148        dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n",
 149                gmouse->scan_ms,
 150                gmouse->bleft ? "" : "left ",
 151                gmouse->bmiddle ? "" : "middle ",
 152                gmouse->bright ? "" : "right");
 153
 154        return 0;
 155}
 156
 157static const struct of_device_id gpio_mouse_of_match[] = {
 158        { .compatible = "gpio-mouse", },
 159        { },
 160};
 161MODULE_DEVICE_TABLE(of, gpio_mouse_of_match);
 162
 163static struct platform_driver gpio_mouse_device_driver = {
 164        .probe          = gpio_mouse_probe,
 165        .driver         = {
 166                .name   = "gpio_mouse",
 167                .of_match_table = gpio_mouse_of_match,
 168        }
 169};
 170module_platform_driver(gpio_mouse_device_driver);
 171
 172MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
 173MODULE_DESCRIPTION("GPIO mouse driver");
 174MODULE_LICENSE("GPL");
 175MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */
 176