Commit 0e7d0c860a0dee49dacb7bbb248d1eba637075ad
Committed by
Dmitry Torokhov
1 parent
8ed9e0e1b6
Exists in
master
and in
4 other branches
Input: add input driver for polled GPIO buttons
The existing gpio-keys driver can be usable only for GPIO lines with interrupt support. Several devices have buttons connected to a GPIO line which is not capable to generate interrupts. This patch adds a new input driver using the generic GPIO layer and the input-polldev to support such buttons. [Ben Gardiner <bengardiner@nanometrics.ca: fold code to use more of the original gpio_keys infrastructure; cleanups and other improvements.] Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Signed-off-by: Ben Gardiner <bengardiner@nanometrics.ca> Tested-by: Ben Gardiner <bengardiner@nanometrics.ca> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Showing 4 changed files with 280 additions and 0 deletions Side-by-side Diff
drivers/input/keyboard/Kconfig
... | ... | @@ -179,6 +179,22 @@ |
179 | 179 | To compile this driver as a module, choose M here: the |
180 | 180 | module will be called gpio_keys. |
181 | 181 | |
182 | +config KEYBOARD_GPIO_POLLED | |
183 | + tristate "Polled GPIO buttons" | |
184 | + depends on GENERIC_GPIO | |
185 | + select INPUT_POLLDEV | |
186 | + help | |
187 | + This driver implements support for buttons connected | |
188 | + to GPIO pins that are not capable of generating interrupts. | |
189 | + | |
190 | + Say Y here if your device has buttons connected | |
191 | + directly to such GPIO pins. Your board-specific | |
192 | + setup logic must also provide a platform device, | |
193 | + with configuration data saying which GPIOs are used. | |
194 | + | |
195 | + To compile this driver as a module, choose M here: the | |
196 | + module will be called gpio_keys_polled. | |
197 | + | |
182 | 198 | config KEYBOARD_TCA6416 |
183 | 199 | tristate "TCA6416 Keypad Support" |
184 | 200 | depends on I2C |
drivers/input/keyboard/Makefile
... | ... | @@ -14,6 +14,7 @@ |
14 | 14 | obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o |
15 | 15 | obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o |
16 | 16 | obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o |
17 | +obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o | |
17 | 18 | obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o |
18 | 19 | obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o |
19 | 20 | obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o |
drivers/input/keyboard/gpio_keys_polled.c
1 | +/* | |
2 | + * Driver for buttons on GPIO lines not capable of generating interrupts | |
3 | + * | |
4 | + * Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org> | |
5 | + * Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com> | |
6 | + * | |
7 | + * This file was based on: /drivers/input/misc/cobalt_btns.c | |
8 | + * Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> | |
9 | + * | |
10 | + * also was based on: /drivers/input/keyboard/gpio_keys.c | |
11 | + * Copyright 2005 Phil Blundell | |
12 | + * | |
13 | + * This program is free software; you can redistribute it and/or modify | |
14 | + * it under the terms of the GNU General Public License version 2 as | |
15 | + * published by the Free Software Foundation. | |
16 | + */ | |
17 | + | |
18 | +#include <linux/kernel.h> | |
19 | +#include <linux/module.h> | |
20 | +#include <linux/init.h> | |
21 | +#include <linux/slab.h> | |
22 | +#include <linux/input.h> | |
23 | +#include <linux/input-polldev.h> | |
24 | +#include <linux/ioport.h> | |
25 | +#include <linux/platform_device.h> | |
26 | +#include <linux/gpio.h> | |
27 | +#include <linux/gpio_keys.h> | |
28 | + | |
29 | +#define DRV_NAME "gpio-keys-polled" | |
30 | + | |
31 | +struct gpio_keys_button_data { | |
32 | + int last_state; | |
33 | + int count; | |
34 | + int threshold; | |
35 | + int can_sleep; | |
36 | +}; | |
37 | + | |
38 | +struct gpio_keys_polled_dev { | |
39 | + struct input_polled_dev *poll_dev; | |
40 | + struct device *dev; | |
41 | + struct gpio_keys_platform_data *pdata; | |
42 | + struct gpio_keys_button_data data[0]; | |
43 | +}; | |
44 | + | |
45 | +static void gpio_keys_polled_check_state(struct input_dev *input, | |
46 | + struct gpio_keys_button *button, | |
47 | + struct gpio_keys_button_data *bdata) | |
48 | +{ | |
49 | + int state; | |
50 | + | |
51 | + if (bdata->can_sleep) | |
52 | + state = !!gpio_get_value_cansleep(button->gpio); | |
53 | + else | |
54 | + state = !!gpio_get_value(button->gpio); | |
55 | + | |
56 | + if (state != bdata->last_state) { | |
57 | + unsigned int type = button->type ?: EV_KEY; | |
58 | + | |
59 | + input_event(input, type, button->code, | |
60 | + !!(state ^ button->active_low)); | |
61 | + input_sync(input); | |
62 | + bdata->count = 0; | |
63 | + bdata->last_state = state; | |
64 | + } | |
65 | +} | |
66 | + | |
67 | +static void gpio_keys_polled_poll(struct input_polled_dev *dev) | |
68 | +{ | |
69 | + struct gpio_keys_polled_dev *bdev = dev->private; | |
70 | + struct gpio_keys_platform_data *pdata = bdev->pdata; | |
71 | + struct input_dev *input = dev->input; | |
72 | + int i; | |
73 | + | |
74 | + for (i = 0; i < bdev->pdata->nbuttons; i++) { | |
75 | + struct gpio_keys_button_data *bdata = &bdev->data[i]; | |
76 | + | |
77 | + if (bdata->count < bdata->threshold) | |
78 | + bdata->count++; | |
79 | + else | |
80 | + gpio_keys_polled_check_state(input, &pdata->buttons[i], | |
81 | + bdata); | |
82 | + } | |
83 | +} | |
84 | + | |
85 | +static void gpio_keys_polled_open(struct input_polled_dev *dev) | |
86 | +{ | |
87 | + struct gpio_keys_polled_dev *bdev = dev->private; | |
88 | + struct gpio_keys_platform_data *pdata = bdev->pdata; | |
89 | + | |
90 | + if (pdata->enable) | |
91 | + pdata->enable(bdev->dev); | |
92 | +} | |
93 | + | |
94 | +static void gpio_keys_polled_close(struct input_polled_dev *dev) | |
95 | +{ | |
96 | + struct gpio_keys_polled_dev *bdev = dev->private; | |
97 | + struct gpio_keys_platform_data *pdata = bdev->pdata; | |
98 | + | |
99 | + if (pdata->disable) | |
100 | + pdata->disable(bdev->dev); | |
101 | +} | |
102 | + | |
103 | +static int __devinit gpio_keys_polled_probe(struct platform_device *pdev) | |
104 | +{ | |
105 | + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; | |
106 | + struct device *dev = &pdev->dev; | |
107 | + struct gpio_keys_polled_dev *bdev; | |
108 | + struct input_polled_dev *poll_dev; | |
109 | + struct input_dev *input; | |
110 | + int error; | |
111 | + int i; | |
112 | + | |
113 | + if (!pdata || !pdata->poll_interval) | |
114 | + return -EINVAL; | |
115 | + | |
116 | + bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) + | |
117 | + pdata->nbuttons * sizeof(struct gpio_keys_button_data), | |
118 | + GFP_KERNEL); | |
119 | + if (!bdev) { | |
120 | + dev_err(dev, "no memory for private data\n"); | |
121 | + return -ENOMEM; | |
122 | + } | |
123 | + | |
124 | + poll_dev = input_allocate_polled_device(); | |
125 | + if (!poll_dev) { | |
126 | + dev_err(dev, "no memory for polled device\n"); | |
127 | + error = -ENOMEM; | |
128 | + goto err_free_bdev; | |
129 | + } | |
130 | + | |
131 | + poll_dev->private = bdev; | |
132 | + poll_dev->poll = gpio_keys_polled_poll; | |
133 | + poll_dev->poll_interval = pdata->poll_interval; | |
134 | + poll_dev->open = gpio_keys_polled_open; | |
135 | + poll_dev->close = gpio_keys_polled_close; | |
136 | + | |
137 | + input = poll_dev->input; | |
138 | + | |
139 | + input->evbit[0] = BIT(EV_KEY); | |
140 | + input->name = pdev->name; | |
141 | + input->phys = DRV_NAME"/input0"; | |
142 | + input->dev.parent = &pdev->dev; | |
143 | + | |
144 | + input->id.bustype = BUS_HOST; | |
145 | + input->id.vendor = 0x0001; | |
146 | + input->id.product = 0x0001; | |
147 | + input->id.version = 0x0100; | |
148 | + | |
149 | + for (i = 0; i < pdata->nbuttons; i++) { | |
150 | + struct gpio_keys_button *button = &pdata->buttons[i]; | |
151 | + struct gpio_keys_button_data *bdata = &bdev->data[i]; | |
152 | + unsigned int gpio = button->gpio; | |
153 | + unsigned int type = button->type ?: EV_KEY; | |
154 | + | |
155 | + if (button->wakeup) { | |
156 | + dev_err(dev, DRV_NAME " does not support wakeup\n"); | |
157 | + error = -EINVAL; | |
158 | + goto err_free_gpio; | |
159 | + } | |
160 | + | |
161 | + error = gpio_request(gpio, | |
162 | + button->desc ? button->desc : DRV_NAME); | |
163 | + if (error) { | |
164 | + dev_err(dev, "unable to claim gpio %u, err=%d\n", | |
165 | + gpio, error); | |
166 | + goto err_free_gpio; | |
167 | + } | |
168 | + | |
169 | + error = gpio_direction_input(gpio); | |
170 | + if (error) { | |
171 | + dev_err(dev, | |
172 | + "unable to set direction on gpio %u, err=%d\n", | |
173 | + gpio, error); | |
174 | + goto err_free_gpio; | |
175 | + } | |
176 | + | |
177 | + bdata->can_sleep = gpio_cansleep(gpio); | |
178 | + bdata->last_state = -1; | |
179 | + bdata->threshold = DIV_ROUND_UP(button->debounce_interval, | |
180 | + pdata->poll_interval); | |
181 | + | |
182 | + input_set_capability(input, type, button->code); | |
183 | + } | |
184 | + | |
185 | + bdev->poll_dev = poll_dev; | |
186 | + bdev->dev = dev; | |
187 | + bdev->pdata = pdata; | |
188 | + platform_set_drvdata(pdev, bdev); | |
189 | + | |
190 | + error = input_register_polled_device(poll_dev); | |
191 | + if (error) { | |
192 | + dev_err(dev, "unable to register polled device, err=%d\n", | |
193 | + error); | |
194 | + goto err_free_gpio; | |
195 | + } | |
196 | + | |
197 | + /* report initial state of the buttons */ | |
198 | + for (i = 0; i < pdata->nbuttons; i++) | |
199 | + gpio_keys_polled_check_state(input, &pdata->buttons[i], | |
200 | + &bdev->data[i]); | |
201 | + | |
202 | + return 0; | |
203 | + | |
204 | +err_free_gpio: | |
205 | + while (--i >= 0) | |
206 | + gpio_free(pdata->buttons[i].gpio); | |
207 | + | |
208 | + input_free_polled_device(poll_dev); | |
209 | + | |
210 | +err_free_bdev: | |
211 | + kfree(bdev); | |
212 | + | |
213 | + platform_set_drvdata(pdev, NULL); | |
214 | + return error; | |
215 | +} | |
216 | + | |
217 | +static int __devexit gpio_keys_polled_remove(struct platform_device *pdev) | |
218 | +{ | |
219 | + struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev); | |
220 | + struct gpio_keys_platform_data *pdata = bdev->pdata; | |
221 | + int i; | |
222 | + | |
223 | + input_unregister_polled_device(bdev->poll_dev); | |
224 | + | |
225 | + for (i = 0; i < pdata->nbuttons; i++) | |
226 | + gpio_free(pdata->buttons[i].gpio); | |
227 | + | |
228 | + input_free_polled_device(bdev->poll_dev); | |
229 | + | |
230 | + kfree(bdev); | |
231 | + platform_set_drvdata(pdev, NULL); | |
232 | + | |
233 | + return 0; | |
234 | +} | |
235 | + | |
236 | +static struct platform_driver gpio_keys_polled_driver = { | |
237 | + .probe = gpio_keys_polled_probe, | |
238 | + .remove = __devexit_p(gpio_keys_polled_remove), | |
239 | + .driver = { | |
240 | + .name = DRV_NAME, | |
241 | + .owner = THIS_MODULE, | |
242 | + }, | |
243 | +}; | |
244 | + | |
245 | +static int __init gpio_keys_polled_init(void) | |
246 | +{ | |
247 | + return platform_driver_register(&gpio_keys_polled_driver); | |
248 | +} | |
249 | + | |
250 | +static void __exit gpio_keys_polled_exit(void) | |
251 | +{ | |
252 | + platform_driver_unregister(&gpio_keys_polled_driver); | |
253 | +} | |
254 | + | |
255 | +module_init(gpio_keys_polled_init); | |
256 | +module_exit(gpio_keys_polled_exit); | |
257 | + | |
258 | +MODULE_LICENSE("GPL v2"); | |
259 | +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); | |
260 | +MODULE_DESCRIPTION("Polled GPIO Buttons driver"); | |
261 | +MODULE_ALIAS("platform:" DRV_NAME); |
include/linux/gpio_keys.h
... | ... | @@ -16,6 +16,8 @@ |
16 | 16 | struct gpio_keys_platform_data { |
17 | 17 | struct gpio_keys_button *buttons; |
18 | 18 | int nbuttons; |
19 | + unsigned int poll_interval; /* polling interval in msecs - | |
20 | + for polling driver only */ | |
19 | 21 | unsigned int rep:1; /* enable input subsystem auto repeat */ |
20 | 22 | int (*enable)(struct device *dev); |
21 | 23 | void (*disable)(struct device *dev); |