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