blob: 7be0cb5214bbc03b43d960f221d54c074abab468 [file] [log] [blame]
Arve Hjønnevåg72fc6242008-10-15 18:23:47 -07001/* drivers/input/misc/gpio_event.c
2 *
3 * Copyright (C) 2007 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/earlysuspend.h>
17#include <linux/module.h>
18#include <linux/input.h>
19#include <linux/gpio_event.h>
20#include <linux/hrtimer.h>
21#include <linux/platform_device.h>
22#include <linux/slab.h>
23
24struct gpio_event {
25 struct input_dev *input_dev;
26 const struct gpio_event_platform_data *info;
27 struct early_suspend early_suspend;
28 void *state[0];
29};
30
31static int gpio_input_event(
32 struct input_dev *dev, unsigned int type, unsigned int code, int value)
33{
34 int i;
35 int ret = 0;
36 int tmp_ret;
37 struct gpio_event_info **ii;
38 struct gpio_event *ip = input_get_drvdata(dev);
39
40 for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) {
41 if ((*ii)->event) {
42 tmp_ret = (*ii)->event(ip->input_dev, *ii,
43 &ip->state[i], type, code, value);
44 if (tmp_ret)
45 ret = tmp_ret;
46 }
47 }
48 return ret;
49}
50
51static int gpio_event_call_all_func(struct gpio_event *ip, int func)
52{
53 int i;
54 int ret;
55 struct gpio_event_info **ii;
56
57 if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) {
58 ii = ip->info->info;
59 for (i = 0; i < ip->info->info_count; i++, ii++) {
60 if ((*ii)->func == NULL) {
61 ret = -ENODEV;
62 pr_err("gpio_event_probe: Incomplete pdata, "
63 "no function\n");
64 goto err_no_func;
65 }
66 ret = (*ii)->func(ip->input_dev, *ii, &ip->state[i],
67 func);
68 if (ret) {
69 pr_err("gpio_event_probe: function failed\n");
70 goto err_func_failed;
71 }
72 }
73 return 0;
74 }
75
76 ret = 0;
77 i = ip->info->info_count;
78 ii = ip->info->info + i;
79 while (i > 0) {
80 i--;
81 ii--;
82 (*ii)->func(ip->input_dev, *ii, &ip->state[i], func & ~1);
83err_func_failed:
84err_no_func:
85 ;
86 }
87 return ret;
88}
89
90#ifdef CONFIG_HAS_EARLYSUSPEND
91void gpio_event_suspend(struct early_suspend *h)
92{
93 struct gpio_event *ip;
94 ip = container_of(h, struct gpio_event, early_suspend);
95 gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND);
96 ip->info->power(ip->info, 0);
97}
98
99void gpio_event_resume(struct early_suspend *h)
100{
101 struct gpio_event *ip;
102 ip = container_of(h, struct gpio_event, early_suspend);
103 ip->info->power(ip->info, 1);
104 gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME);
105}
106#endif
107
108static int __init gpio_event_probe(struct platform_device *pdev)
109{
110 int err;
111 struct gpio_event *ip;
112 struct input_dev *input_dev;
113 struct gpio_event_platform_data *event_info;
114
115 event_info = pdev->dev.platform_data;
116 if (event_info == NULL) {
117 pr_err("gpio_event_probe: No pdata\n");
118 return -ENODEV;
119 }
120 if (event_info->name == NULL ||
121 event_info->info == NULL ||
122 event_info->info_count == 0) {
123 pr_err("gpio_event_probe: Incomplete pdata\n");
124 return -ENODEV;
125 }
126 ip = kzalloc(sizeof(*ip) +
127 sizeof(ip->state[0]) * event_info->info_count, GFP_KERNEL);
128 if (ip == NULL) {
129 err = -ENOMEM;
130 pr_err("gpio_event_probe: Failed to allocate private data\n");
131 goto err_kp_alloc_failed;
132 }
133 platform_set_drvdata(pdev, ip);
134
135 input_dev = input_allocate_device();
136 if (input_dev == NULL) {
137 err = -ENOMEM;
138 pr_err("gpio_event_probe: Failed to allocate input device\n");
139 goto err_input_dev_alloc_failed;
140 }
141 input_set_drvdata(input_dev, ip);
142 ip->input_dev = input_dev;
143 ip->info = event_info;
144 if (event_info->power) {
145#ifdef CONFIG_HAS_EARLYSUSPEND
146 ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
147 ip->early_suspend.suspend = gpio_event_suspend;
148 ip->early_suspend.resume = gpio_event_resume;
149 register_early_suspend(&ip->early_suspend);
150#endif
151 ip->info->power(ip->info, 1);
152 }
153
154 input_dev->name = ip->info->name;
155 input_dev->event = gpio_input_event;
156
157 err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT);
158 if (err)
159 goto err_call_all_func_failed;
160
161 err = input_register_device(input_dev);
162 if (err) {
163 pr_err("gpio_event_probe: Unable to register %s input device\n",
164 input_dev->name);
165 goto err_input_register_device_failed;
166 }
167
168 return 0;
169
170err_input_register_device_failed:
171 gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
172err_call_all_func_failed:
173 if (event_info->power) {
174#ifdef CONFIG_HAS_EARLYSUSPEND
175 unregister_early_suspend(&ip->early_suspend);
176#endif
177 ip->info->power(ip->info, 0);
178 }
179 input_free_device(input_dev);
180err_input_dev_alloc_failed:
181 kfree(ip);
182err_kp_alloc_failed:
183 return err;
184}
185
186static int gpio_event_remove(struct platform_device *pdev)
187{
188 struct gpio_event *ip = platform_get_drvdata(pdev);
189
190 gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
191 if (ip->info->power) {
192#ifdef CONFIG_HAS_EARLYSUSPEND
193 unregister_early_suspend(&ip->early_suspend);
194#endif
195 ip->info->power(ip->info, 0);
196 }
197 input_unregister_device(ip->input_dev);
198 kfree(ip);
199 return 0;
200}
201
202static struct platform_driver gpio_event_driver = {
203 .probe = gpio_event_probe,
204 .remove = gpio_event_remove,
205 .driver = {
206 .name = GPIO_EVENT_DEV_NAME,
207 },
208};
209
210static int __devinit gpio_event_init(void)
211{
212 return platform_driver_register(&gpio_event_driver);
213}
214
215static void __exit gpio_event_exit(void)
216{
217 platform_driver_unregister(&gpio_event_driver);
218}
219
220module_init(gpio_event_init);
221module_exit(gpio_event_exit);
222
223MODULE_DESCRIPTION("GPIO Event Driver");
224MODULE_LICENSE("GPL");
225