xref: /linux/drivers/input/mouse/gpio_mouse.c (revision 0de048aba1b751ee19a747dc4c82636579e47b85)
15f565502SHans-Christian Egtvedt /*
25f565502SHans-Christian Egtvedt  * Driver for simulating a mouse on GPIO lines.
35f565502SHans-Christian Egtvedt  *
45f565502SHans-Christian Egtvedt  * Copyright (C) 2007 Atmel Corporation
55f565502SHans-Christian Egtvedt  *
65f565502SHans-Christian Egtvedt  * This program is free software; you can redistribute it and/or modify
75f565502SHans-Christian Egtvedt  * it under the terms of the GNU General Public License version 2 as
85f565502SHans-Christian Egtvedt  * published by the Free Software Foundation.
95f565502SHans-Christian Egtvedt  */
105f565502SHans-Christian Egtvedt 
115f565502SHans-Christian Egtvedt #include <linux/init.h>
125f565502SHans-Christian Egtvedt #include <linux/module.h>
135f565502SHans-Christian Egtvedt #include <linux/platform_device.h>
145f565502SHans-Christian Egtvedt #include <linux/input-polldev.h>
155f565502SHans-Christian Egtvedt #include <linux/gpio_mouse.h>
165f565502SHans-Christian Egtvedt 
175f565502SHans-Christian Egtvedt #include <asm/gpio.h>
185f565502SHans-Christian Egtvedt 
195f565502SHans-Christian Egtvedt /*
205f565502SHans-Christian Egtvedt  * Timer function which is run every scan_ms ms when the device is opened.
215f565502SHans-Christian Egtvedt  * The dev input varaible is set to the the input_dev pointer.
225f565502SHans-Christian Egtvedt  */
235f565502SHans-Christian Egtvedt static void gpio_mouse_scan(struct input_polled_dev *dev)
245f565502SHans-Christian Egtvedt {
255f565502SHans-Christian Egtvedt 	struct gpio_mouse_platform_data *gpio = dev->private;
265f565502SHans-Christian Egtvedt 	struct input_dev *input = dev->input;
275f565502SHans-Christian Egtvedt 	int x, y;
285f565502SHans-Christian Egtvedt 
295f565502SHans-Christian Egtvedt 	if (gpio->bleft >= 0)
305f565502SHans-Christian Egtvedt 		input_report_key(input, BTN_LEFT,
315f565502SHans-Christian Egtvedt 				gpio_get_value(gpio->bleft) ^ gpio->polarity);
325f565502SHans-Christian Egtvedt 	if (gpio->bmiddle >= 0)
335f565502SHans-Christian Egtvedt 		input_report_key(input, BTN_MIDDLE,
345f565502SHans-Christian Egtvedt 				gpio_get_value(gpio->bmiddle) ^ gpio->polarity);
355f565502SHans-Christian Egtvedt 	if (gpio->bright >= 0)
365f565502SHans-Christian Egtvedt 		input_report_key(input, BTN_RIGHT,
375f565502SHans-Christian Egtvedt 				gpio_get_value(gpio->bright) ^ gpio->polarity);
385f565502SHans-Christian Egtvedt 
395f565502SHans-Christian Egtvedt 	x = (gpio_get_value(gpio->right) ^ gpio->polarity)
405f565502SHans-Christian Egtvedt 		- (gpio_get_value(gpio->left) ^ gpio->polarity);
415f565502SHans-Christian Egtvedt 	y = (gpio_get_value(gpio->down) ^ gpio->polarity)
425f565502SHans-Christian Egtvedt 		- (gpio_get_value(gpio->up) ^ gpio->polarity);
435f565502SHans-Christian Egtvedt 
445f565502SHans-Christian Egtvedt 	input_report_rel(input, REL_X, x);
455f565502SHans-Christian Egtvedt 	input_report_rel(input, REL_Y, y);
465f565502SHans-Christian Egtvedt 	input_sync(input);
475f565502SHans-Christian Egtvedt }
485f565502SHans-Christian Egtvedt 
495f565502SHans-Christian Egtvedt static int __init gpio_mouse_probe(struct platform_device *pdev)
505f565502SHans-Christian Egtvedt {
515f565502SHans-Christian Egtvedt 	struct gpio_mouse_platform_data *pdata = pdev->dev.platform_data;
525f565502SHans-Christian Egtvedt 	struct input_polled_dev *input_poll;
535f565502SHans-Christian Egtvedt 	struct input_dev *input;
545f565502SHans-Christian Egtvedt 	int pin, i;
555f565502SHans-Christian Egtvedt 	int error;
565f565502SHans-Christian Egtvedt 
575f565502SHans-Christian Egtvedt 	if (!pdata) {
585f565502SHans-Christian Egtvedt 		dev_err(&pdev->dev, "no platform data\n");
595f565502SHans-Christian Egtvedt 		error = -ENXIO;
605f565502SHans-Christian Egtvedt 		goto out;
615f565502SHans-Christian Egtvedt 	}
625f565502SHans-Christian Egtvedt 
635f565502SHans-Christian Egtvedt 	if (pdata->scan_ms < 0) {
645f565502SHans-Christian Egtvedt 		dev_err(&pdev->dev, "invalid scan time\n");
655f565502SHans-Christian Egtvedt 		error = -EINVAL;
665f565502SHans-Christian Egtvedt 		goto out;
675f565502SHans-Christian Egtvedt 	}
685f565502SHans-Christian Egtvedt 
695f565502SHans-Christian Egtvedt 	for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) {
705f565502SHans-Christian Egtvedt 		pin = pdata->pins[i];
715f565502SHans-Christian Egtvedt 
725f565502SHans-Christian Egtvedt 		if (pin < 0) {
735f565502SHans-Christian Egtvedt 
745f565502SHans-Christian Egtvedt 			if (i <= GPIO_MOUSE_PIN_RIGHT) {
755f565502SHans-Christian Egtvedt 				/* Mouse direction is required. */
765f565502SHans-Christian Egtvedt 				dev_err(&pdev->dev,
775f565502SHans-Christian Egtvedt 					"missing GPIO for directions\n");
785f565502SHans-Christian Egtvedt 				error = -EINVAL;
795f565502SHans-Christian Egtvedt 				goto out_free_gpios;
805f565502SHans-Christian Egtvedt 			}
815f565502SHans-Christian Egtvedt 
825f565502SHans-Christian Egtvedt 			if (i == GPIO_MOUSE_PIN_BLEFT)
835f565502SHans-Christian Egtvedt 				dev_dbg(&pdev->dev, "no left button defined\n");
845f565502SHans-Christian Egtvedt 
855f565502SHans-Christian Egtvedt 		} else {
865f565502SHans-Christian Egtvedt 			error = gpio_request(pin, "gpio_mouse");
875f565502SHans-Christian Egtvedt 			if (error) {
885f565502SHans-Christian Egtvedt 				dev_err(&pdev->dev, "fail %d pin (%d idx)\n",
895f565502SHans-Christian Egtvedt 					pin, i);
905f565502SHans-Christian Egtvedt 				goto out_free_gpios;
915f565502SHans-Christian Egtvedt 			}
925f565502SHans-Christian Egtvedt 
935f565502SHans-Christian Egtvedt 			gpio_direction_input(pin);
945f565502SHans-Christian Egtvedt 		}
955f565502SHans-Christian Egtvedt 	}
965f565502SHans-Christian Egtvedt 
975f565502SHans-Christian Egtvedt 	input_poll = input_allocate_polled_device();
985f565502SHans-Christian Egtvedt 	if (!input_poll) {
995f565502SHans-Christian Egtvedt 		dev_err(&pdev->dev, "not enough memory for input device\n");
1005f565502SHans-Christian Egtvedt 		error = -ENOMEM;
1015f565502SHans-Christian Egtvedt 		goto out_free_gpios;
1025f565502SHans-Christian Egtvedt 	}
1035f565502SHans-Christian Egtvedt 
1045f565502SHans-Christian Egtvedt 	platform_set_drvdata(pdev, input_poll);
1055f565502SHans-Christian Egtvedt 
1065f565502SHans-Christian Egtvedt 	/* set input-polldev handlers */
1075f565502SHans-Christian Egtvedt 	input_poll->private = pdata;
1085f565502SHans-Christian Egtvedt 	input_poll->poll = gpio_mouse_scan;
1095f565502SHans-Christian Egtvedt 	input_poll->poll_interval = pdata->scan_ms;
1105f565502SHans-Christian Egtvedt 
1115f565502SHans-Christian Egtvedt 	input = input_poll->input;
1125f565502SHans-Christian Egtvedt 	input->name = pdev->name;
1135f565502SHans-Christian Egtvedt 	input->id.bustype = BUS_HOST;
1145f565502SHans-Christian Egtvedt 	input->dev.parent = &pdev->dev;
1155f565502SHans-Christian Egtvedt 
1165f565502SHans-Christian Egtvedt 	input_set_capability(input, EV_REL, REL_X);
1175f565502SHans-Christian Egtvedt 	input_set_capability(input, EV_REL, REL_Y);
1185f565502SHans-Christian Egtvedt 	if (pdata->bleft >= 0)
1195f565502SHans-Christian Egtvedt 		input_set_capability(input, EV_KEY, BTN_LEFT);
1205f565502SHans-Christian Egtvedt 	if (pdata->bmiddle >= 0)
1215f565502SHans-Christian Egtvedt 		input_set_capability(input, EV_KEY, BTN_MIDDLE);
1225f565502SHans-Christian Egtvedt 	if (pdata->bright >= 0)
1235f565502SHans-Christian Egtvedt 		input_set_capability(input, EV_KEY, BTN_RIGHT);
1245f565502SHans-Christian Egtvedt 
1255f565502SHans-Christian Egtvedt 	error = input_register_polled_device(input_poll);
1265f565502SHans-Christian Egtvedt 	if (error) {
1275f565502SHans-Christian Egtvedt 		dev_err(&pdev->dev, "could not register input device\n");
1285f565502SHans-Christian Egtvedt 		goto out_free_polldev;
1295f565502SHans-Christian Egtvedt 	}
1305f565502SHans-Christian Egtvedt 
1315f565502SHans-Christian Egtvedt 	dev_dbg(&pdev->dev, "%d ms scan time, buttons: %s%s%s\n",
1325f565502SHans-Christian Egtvedt 			pdata->scan_ms,
1335f565502SHans-Christian Egtvedt 			pdata->bleft < 0 ? "" : "left ",
1345f565502SHans-Christian Egtvedt 			pdata->bmiddle < 0 ? "" : "middle ",
1355f565502SHans-Christian Egtvedt 			pdata->bright < 0 ? "" : "right");
1365f565502SHans-Christian Egtvedt 
1375f565502SHans-Christian Egtvedt 	return 0;
1385f565502SHans-Christian Egtvedt 
1395f565502SHans-Christian Egtvedt  out_free_polldev:
1405f565502SHans-Christian Egtvedt 	input_free_polled_device(input_poll);
1415f565502SHans-Christian Egtvedt 	platform_set_drvdata(pdev, NULL);
1425f565502SHans-Christian Egtvedt 
1435f565502SHans-Christian Egtvedt  out_free_gpios:
1445f565502SHans-Christian Egtvedt 	while (--i >= 0) {
1455f565502SHans-Christian Egtvedt 		pin = pdata->pins[i];
1465f565502SHans-Christian Egtvedt 		if (pin)
1475f565502SHans-Christian Egtvedt 			gpio_free(pin);
1485f565502SHans-Christian Egtvedt 	}
1495f565502SHans-Christian Egtvedt  out:
1505f565502SHans-Christian Egtvedt 	return error;
1515f565502SHans-Christian Egtvedt }
1525f565502SHans-Christian Egtvedt 
1535f565502SHans-Christian Egtvedt static int __devexit gpio_mouse_remove(struct platform_device *pdev)
1545f565502SHans-Christian Egtvedt {
1555f565502SHans-Christian Egtvedt 	struct input_polled_dev *input = platform_get_drvdata(pdev);
1565f565502SHans-Christian Egtvedt 	struct gpio_mouse_platform_data *pdata = input->private;
1575f565502SHans-Christian Egtvedt 	int pin, i;
1585f565502SHans-Christian Egtvedt 
1595f565502SHans-Christian Egtvedt 	input_unregister_polled_device(input);
1605f565502SHans-Christian Egtvedt 	input_free_polled_device(input);
1615f565502SHans-Christian Egtvedt 
1625f565502SHans-Christian Egtvedt 	for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) {
1635f565502SHans-Christian Egtvedt 		pin = pdata->pins[i];
1645f565502SHans-Christian Egtvedt 		if (pin >= 0)
1655f565502SHans-Christian Egtvedt 			gpio_free(pin);
1665f565502SHans-Christian Egtvedt 	}
1675f565502SHans-Christian Egtvedt 
1685f565502SHans-Christian Egtvedt 	platform_set_drvdata(pdev, NULL);
1695f565502SHans-Christian Egtvedt 
1705f565502SHans-Christian Egtvedt 	return 0;
1715f565502SHans-Christian Egtvedt }
1725f565502SHans-Christian Egtvedt 
173d7b5247bSKay Sievers /* work with hotplug and coldplug */
174d7b5247bSKay Sievers MODULE_ALIAS("platform:gpio_mouse");
175d7b5247bSKay Sievers 
176*0de048abSRoel Kluin static struct platform_driver gpio_mouse_device_driver = {
1775f565502SHans-Christian Egtvedt 	.remove		= __devexit_p(gpio_mouse_remove),
1785f565502SHans-Christian Egtvedt 	.driver		= {
1795f565502SHans-Christian Egtvedt 		.name	= "gpio_mouse",
180d7b5247bSKay Sievers 		.owner	= THIS_MODULE,
1815f565502SHans-Christian Egtvedt 	}
1825f565502SHans-Christian Egtvedt };
1835f565502SHans-Christian Egtvedt 
1845f565502SHans-Christian Egtvedt static int __init gpio_mouse_init(void)
1855f565502SHans-Christian Egtvedt {
1865f565502SHans-Christian Egtvedt 	return platform_driver_probe(&gpio_mouse_device_driver,
1875f565502SHans-Christian Egtvedt 			gpio_mouse_probe);
1885f565502SHans-Christian Egtvedt }
1895f565502SHans-Christian Egtvedt module_init(gpio_mouse_init);
1905f565502SHans-Christian Egtvedt 
1915f565502SHans-Christian Egtvedt static void __exit gpio_mouse_exit(void)
1925f565502SHans-Christian Egtvedt {
1935f565502SHans-Christian Egtvedt 	platform_driver_unregister(&gpio_mouse_device_driver);
1945f565502SHans-Christian Egtvedt }
1955f565502SHans-Christian Egtvedt module_exit(gpio_mouse_exit);
1965f565502SHans-Christian Egtvedt 
1975f565502SHans-Christian Egtvedt MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
1985f565502SHans-Christian Egtvedt MODULE_DESCRIPTION("GPIO mouse driver");
1995f565502SHans-Christian Egtvedt MODULE_LICENSE("GPL");
200