]>
Commit | Line | Data |
---|---|---|
420818f9 AH |
1 | /* 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 | ||
ace9e791 | 16 | |
5a0e3ad6 | 17 | #include <linux/slab.h> |
420818f9 AH |
18 | #include <linux/module.h> |
19 | #include <linux/input.h> | |
20 | #include <linux/gpio_event.h> | |
21 | #include <linux/hrtimer.h> | |
22 | #include <linux/platform_device.h> | |
23 | ||
24 | struct gpio_event { | |
25 | struct input_dev *input_dev; | |
26 | const struct gpio_event_platform_data *info; | |
420818f9 AH |
27 | void *state[0]; |
28 | }; | |
29 | ||
30 | static int gpio_input_event( | |
31 | struct input_dev *dev, unsigned int type, unsigned int code, int value) | |
32 | { | |
33 | int i; | |
34 | int ret = 0; | |
35 | int tmp_ret; | |
36 | struct gpio_event_info **ii; | |
37 | struct gpio_event *ip = input_get_drvdata(dev); | |
38 | ||
39 | for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { | |
40 | if ((*ii)->event) { | |
41 | tmp_ret = (*ii)->event(ip->input_dev, *ii, | |
42 | &ip->state[i], type, code, value); | |
43 | if (tmp_ret) | |
44 | ret = tmp_ret; | |
45 | } | |
46 | } | |
47 | return ret; | |
48 | } | |
49 | ||
50 | static int gpio_event_call_all_func(struct gpio_event *ip, int func) | |
51 | { | |
52 | int i; | |
53 | int ret; | |
54 | struct gpio_event_info **ii; | |
55 | ||
56 | if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { | |
57 | ii = ip->info->info; | |
58 | for (i = 0; i < ip->info->info_count; i++, ii++) { | |
59 | if ((*ii)->func == NULL) { | |
60 | ret = -ENODEV; | |
61 | pr_err("gpio_event_probe: Incomplete pdata, " | |
62 | "no function\n"); | |
63 | goto err_no_func; | |
64 | } | |
65 | ret = (*ii)->func(ip->input_dev, *ii, &ip->state[i], | |
66 | func); | |
67 | if (ret) { | |
68 | pr_err("gpio_event_probe: function failed\n"); | |
69 | goto err_func_failed; | |
70 | } | |
71 | } | |
72 | return 0; | |
73 | } | |
74 | ||
75 | ret = 0; | |
76 | i = ip->info->info_count; | |
77 | ii = ip->info->info + i; | |
78 | while (i > 0) { | |
79 | i--; | |
80 | ii--; | |
81 | (*ii)->func(ip->input_dev, *ii, &ip->state[i], func & ~1); | |
82 | err_func_failed: | |
83 | err_no_func: | |
84 | ; | |
85 | } | |
86 | return ret; | |
87 | } | |
88 | ||
89 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
90 | void gpio_event_suspend(struct early_suspend *h) | |
91 | { | |
92 | struct gpio_event *ip; | |
93 | ip = container_of(h, struct gpio_event, early_suspend); | |
94 | gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); | |
95 | ip->info->power(ip->info, 0); | |
96 | } | |
97 | ||
98 | void gpio_event_resume(struct early_suspend *h) | |
99 | { | |
100 | struct gpio_event *ip; | |
101 | ip = container_of(h, struct gpio_event, early_suspend); | |
102 | ip->info->power(ip->info, 1); | |
103 | gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); | |
104 | } | |
105 | #endif | |
106 | ||
107 | static int __init gpio_event_probe(struct platform_device *pdev) | |
108 | { | |
109 | int err; | |
110 | struct gpio_event *ip; | |
111 | struct input_dev *input_dev; | |
112 | struct gpio_event_platform_data *event_info; | |
113 | ||
114 | event_info = pdev->dev.platform_data; | |
115 | if (event_info == NULL) { | |
116 | pr_err("gpio_event_probe: No pdata\n"); | |
117 | return -ENODEV; | |
118 | } | |
119 | if (event_info->name == NULL || | |
120 | event_info->info == NULL || | |
121 | event_info->info_count == 0) { | |
122 | pr_err("gpio_event_probe: Incomplete pdata\n"); | |
123 | return -ENODEV; | |
124 | } | |
125 | ip = kzalloc(sizeof(*ip) + | |
126 | sizeof(ip->state[0]) * event_info->info_count, GFP_KERNEL); | |
127 | if (ip == NULL) { | |
128 | err = -ENOMEM; | |
129 | pr_err("gpio_event_probe: Failed to allocate private data\n"); | |
130 | goto err_kp_alloc_failed; | |
131 | } | |
132 | platform_set_drvdata(pdev, ip); | |
133 | ||
134 | input_dev = input_allocate_device(); | |
135 | if (input_dev == NULL) { | |
136 | err = -ENOMEM; | |
137 | pr_err("gpio_event_probe: Failed to allocate input device\n"); | |
138 | goto err_input_dev_alloc_failed; | |
139 | } | |
140 | input_set_drvdata(input_dev, ip); | |
141 | ip->input_dev = input_dev; | |
142 | ip->info = event_info; | |
143 | if (event_info->power) { | |
144 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
145 | ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; | |
146 | ip->early_suspend.suspend = gpio_event_suspend; | |
147 | ip->early_suspend.resume = gpio_event_resume; | |
148 | register_early_suspend(&ip->early_suspend); | |
149 | #endif | |
150 | ip->info->power(ip->info, 1); | |
151 | } | |
152 | ||
153 | input_dev->name = ip->info->name; | |
154 | input_dev->event = gpio_input_event; | |
155 | ||
156 | err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); | |
157 | if (err) | |
158 | goto err_call_all_func_failed; | |
159 | ||
160 | err = input_register_device(input_dev); | |
161 | if (err) { | |
162 | pr_err("gpio_event_probe: Unable to register %s input device\n", | |
163 | input_dev->name); | |
164 | goto err_input_register_device_failed; | |
165 | } | |
166 | ||
167 | return 0; | |
168 | ||
169 | err_input_register_device_failed: | |
170 | gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); | |
171 | err_call_all_func_failed: | |
172 | if (event_info->power) { | |
173 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
174 | unregister_early_suspend(&ip->early_suspend); | |
175 | #endif | |
176 | ip->info->power(ip->info, 0); | |
177 | } | |
178 | input_free_device(input_dev); | |
179 | err_input_dev_alloc_failed: | |
180 | kfree(ip); | |
181 | err_kp_alloc_failed: | |
182 | return err; | |
183 | } | |
184 | ||
185 | static int gpio_event_remove(struct platform_device *pdev) | |
186 | { | |
187 | struct gpio_event *ip = platform_get_drvdata(pdev); | |
188 | ||
189 | gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); | |
190 | if (ip->info->power) { | |
191 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
192 | unregister_early_suspend(&ip->early_suspend); | |
193 | #endif | |
194 | ip->info->power(ip->info, 0); | |
195 | } | |
196 | input_unregister_device(ip->input_dev); | |
197 | kfree(ip); | |
198 | return 0; | |
199 | } | |
200 | ||
201 | static struct platform_driver gpio_event_driver = { | |
202 | .probe = gpio_event_probe, | |
203 | .remove = gpio_event_remove, | |
204 | .driver = { | |
205 | .name = GPIO_EVENT_DEV_NAME, | |
206 | }, | |
207 | }; | |
208 | ||
209 | static int __devinit gpio_event_init(void) | |
210 | { | |
211 | return platform_driver_register(&gpio_event_driver); | |
212 | } | |
213 | ||
214 | static void __exit gpio_event_exit(void) | |
215 | { | |
216 | platform_driver_unregister(&gpio_event_driver); | |
217 | } | |
218 | ||
219 | module_init(gpio_event_init); | |
220 | module_exit(gpio_event_exit); | |
221 | ||
222 | MODULE_DESCRIPTION("GPIO Event Driver"); | |
223 | MODULE_LICENSE("GPL"); | |
224 |