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. 21*3a070ad1SUwe Kleine-Koenig * The dev input variable 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 1760de048abSRoel 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