]>
Commit | Line | Data |
---|---|---|
1 | #include <linux/module.h> | |
2 | #include <linux/device.h> | |
3 | #include <linux/platform_device.h> | |
4 | #include <linux/gpio_event.h> | |
5 | #include <mach/gpio.h> | |
6 | ||
7 | /* hardware debounce: (value + 1) * 31us */ | |
8 | #define GPIO_DEBOUNCE_TIME 0x1 | |
9 | ||
10 | #define PREFIX "debounce: " | |
11 | ||
12 | static unsigned old_flags = 0; | |
13 | ktime_t old_debounce_delay; | |
14 | ktime_t old_settle_time; | |
15 | ktime_t old_poll_time; | |
16 | static struct gpio_event_matrix_info *gpio_evmi = NULL; | |
17 | ||
18 | static int find_ms2_dev(struct device *dev, void *data) | |
19 | { | |
20 | if (!strncmp((char*)data, dev_name(dev), strlen((char*)data))) { | |
21 | printk(KERN_INFO PREFIX "Found it\n"); | |
22 | return 1; | |
23 | } | |
24 | return 0; | |
25 | } | |
26 | ||
27 | static ssize_t show_debounce_delay(struct device *dev, struct device_attribute *attr, char *buf) | |
28 | { | |
29 | if (!gpio_evmi) | |
30 | return -ENODEV; | |
31 | ||
32 | return snprintf(buf, PAGE_SIZE, "%ld\n", (gpio_evmi->debounce_delay.tv.nsec / NSEC_PER_MSEC)); | |
33 | } | |
34 | ||
35 | static void set_debounce_delay(long delay) | |
36 | { | |
37 | if (gpio_evmi->debounce_delay.tv.nsec != delay * NSEC_PER_MSEC) { | |
38 | printk(KERN_INFO PREFIX "Changing debounce_delay\n"); | |
39 | gpio_evmi->debounce_delay.tv.nsec = delay * NSEC_PER_MSEC; | |
40 | printk(KERN_INFO PREFIX "debounce_delay: %u\n", gpio_evmi->debounce_delay.tv.nsec); | |
41 | } | |
42 | ||
43 | #if 0 | |
44 | if (gpio_evmi->debounce_delay.tv.nsec != 0) { | |
45 | if (!(gpio_evmi->flags & GPIOKPF_DEBOUNCE)) { | |
46 | printk(KERN_INFO PREFIX "Activating debounce\n"); | |
47 | gpio_evmi->flags |= GPIOKPF_DEBOUNCE; | |
48 | } | |
49 | } else { | |
50 | if (gpio_evmi->flags & GPIOKPF_DEBOUNCE) { | |
51 | printk(KERN_INFO PREFIX "Deactivating debounce\n"); | |
52 | gpio_evmi->flags &= ~GPIOKPF_DEBOUNCE; | |
53 | } | |
54 | } | |
55 | #endif | |
56 | } | |
57 | ||
58 | static ssize_t store_debounce_delay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
59 | { | |
60 | long int delay; | |
61 | ||
62 | if (!gpio_evmi) | |
63 | return -ENODEV; | |
64 | ||
65 | sscanf(buf, "%ld", &delay); | |
66 | set_debounce_delay(delay); | |
67 | ||
68 | return count; | |
69 | } | |
70 | ||
71 | static ssize_t show_settle_time(struct device *dev, struct device_attribute *attr, char *buf) | |
72 | { | |
73 | if (!gpio_evmi) | |
74 | return -ENODEV; | |
75 | ||
76 | return snprintf(buf, PAGE_SIZE, "%ld\n", (gpio_evmi->settle_time.tv.nsec / NSEC_PER_USEC)); | |
77 | } | |
78 | ||
79 | static ssize_t store_settle_time(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
80 | { | |
81 | long int delay; | |
82 | ||
83 | if (!gpio_evmi) | |
84 | return -ENODEV; | |
85 | ||
86 | sscanf(buf, "%ld", &delay); | |
87 | gpio_evmi->settle_time.tv.nsec = delay * NSEC_PER_USEC; | |
88 | ||
89 | return count; | |
90 | } | |
91 | ||
92 | static ssize_t show_poll_time(struct device *dev, struct device_attribute *attr, char *buf) | |
93 | { | |
94 | if (!gpio_evmi) | |
95 | return -ENODEV; | |
96 | ||
97 | return snprintf(buf, PAGE_SIZE, "%ld\n", (gpio_evmi->poll_time.tv.nsec / NSEC_PER_MSEC)); | |
98 | } | |
99 | ||
100 | static ssize_t store_poll_time(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
101 | { | |
102 | long int delay; | |
103 | ||
104 | if (!gpio_evmi) | |
105 | return -ENODEV; | |
106 | ||
107 | sscanf(buf, "%ld", &delay); | |
108 | gpio_evmi->poll_time.tv.nsec = delay * NSEC_PER_MSEC; | |
109 | ||
110 | return count; | |
111 | } | |
112 | ||
113 | static ssize_t show_flags(struct device *dev, struct device_attribute *attr, char *buf) | |
114 | { | |
115 | if (!gpio_evmi) | |
116 | return -ENODEV; | |
117 | ||
118 | return snprintf(buf, PAGE_SIZE, "0x%x\n", gpio_evmi->flags); | |
119 | } | |
120 | ||
121 | static ssize_t store_flags(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
122 | { | |
123 | unsigned flags; | |
124 | ||
125 | if (!gpio_evmi) | |
126 | return -ENODEV; | |
127 | ||
128 | sscanf(buf, "0x%x", &flags); | |
129 | ||
130 | printk(KERN_INFO PREFIX "flags: 0x%x\n", flags); | |
131 | ||
132 | gpio_evmi->flags = flags; | |
133 | ||
134 | return count; | |
135 | } | |
136 | ||
137 | static ssize_t show_debounce_flag(struct device *dev, struct device_attribute *attr, char *buf) | |
138 | { | |
139 | if (!gpio_evmi) | |
140 | return -ENODEV; | |
141 | ||
142 | return snprintf(buf, PAGE_SIZE, "%u\n", (gpio_evmi->flags & GPIOKPF_DEBOUNCE) ? 1 : 0); | |
143 | } | |
144 | ||
145 | static ssize_t store_debounce_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
146 | { | |
147 | unsigned flag; | |
148 | ||
149 | if (!gpio_evmi) | |
150 | return -ENODEV; | |
151 | ||
152 | sscanf(buf, "%u", &flag); | |
153 | ||
154 | if (flag) { | |
155 | gpio_evmi->flags |= GPIOKPF_DEBOUNCE; | |
156 | } else { | |
157 | gpio_evmi->flags &= ~GPIOKPF_DEBOUNCE; | |
158 | } | |
159 | ||
160 | return count; | |
161 | } | |
162 | ||
163 | static ssize_t show_remove_some_phantom_keys_flag(struct device *dev, struct device_attribute *attr, char *buf) | |
164 | { | |
165 | if (!gpio_evmi) | |
166 | return -ENODEV; | |
167 | ||
168 | return snprintf(buf, PAGE_SIZE, "%u\n", (gpio_evmi->flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) ? 1 : 0); | |
169 | } | |
170 | ||
171 | static ssize_t store_remove_some_phantom_keys_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
172 | { | |
173 | unsigned flag; | |
174 | ||
175 | if (!gpio_evmi) | |
176 | return -ENODEV; | |
177 | ||
178 | sscanf(buf, "%u", &flag); | |
179 | ||
180 | if (flag) { | |
181 | gpio_evmi->flags |= GPIOKPF_REMOVE_SOME_PHANTOM_KEYS; | |
182 | } else { | |
183 | gpio_evmi->flags &= ~GPIOKPF_REMOVE_SOME_PHANTOM_KEYS; | |
184 | } | |
185 | ||
186 | return count; | |
187 | } | |
188 | ||
189 | static ssize_t show_print_unmapped_keys_flag(struct device *dev, struct device_attribute *attr, char *buf) | |
190 | { | |
191 | if (!gpio_evmi) | |
192 | return -ENODEV; | |
193 | ||
194 | return snprintf(buf, PAGE_SIZE, "%u\n", (gpio_evmi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) ? 1 : 0); | |
195 | } | |
196 | ||
197 | static ssize_t store_print_unmapped_keys_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
198 | { | |
199 | unsigned flag; | |
200 | ||
201 | if (!gpio_evmi) | |
202 | return -ENODEV; | |
203 | ||
204 | sscanf(buf, "%u", &flag); | |
205 | ||
206 | if (flag) { | |
207 | gpio_evmi->flags |= GPIOKPF_PRINT_UNMAPPED_KEYS; | |
208 | } else { | |
209 | gpio_evmi->flags &= ~GPIOKPF_PRINT_UNMAPPED_KEYS; | |
210 | } | |
211 | ||
212 | return count; | |
213 | } | |
214 | ||
215 | static ssize_t show_print_mapped_keys_flag(struct device *dev, struct device_attribute *attr, char *buf) | |
216 | { | |
217 | if (!gpio_evmi) | |
218 | return -ENODEV; | |
219 | ||
220 | return snprintf(buf, PAGE_SIZE, "%u\n", (gpio_evmi->flags & GPIOKPF_PRINT_MAPPED_KEYS) ? 1 : 0); | |
221 | } | |
222 | ||
223 | static ssize_t store_print_mapped_keys_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
224 | { | |
225 | unsigned flag; | |
226 | ||
227 | if (!gpio_evmi) | |
228 | return -ENODEV; | |
229 | ||
230 | sscanf(buf, "%u", &flag); | |
231 | ||
232 | if (flag) { | |
233 | gpio_evmi->flags |= GPIOKPF_PRINT_MAPPED_KEYS; | |
234 | } else { | |
235 | gpio_evmi->flags &= ~GPIOKPF_PRINT_MAPPED_KEYS; | |
236 | } | |
237 | ||
238 | return count; | |
239 | } | |
240 | ||
241 | static ssize_t show_print_phantom_keys_flag(struct device *dev, struct device_attribute *attr, char *buf) | |
242 | { | |
243 | if (!gpio_evmi) | |
244 | return -ENODEV; | |
245 | ||
246 | return snprintf(buf, PAGE_SIZE, "%u\n", (gpio_evmi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) ? 1 : 0); | |
247 | } | |
248 | ||
249 | static ssize_t store_print_phantom_keys_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
250 | { | |
251 | unsigned flag; | |
252 | ||
253 | if (!gpio_evmi) | |
254 | return -ENODEV; | |
255 | ||
256 | sscanf(buf, "%u", &flag); | |
257 | ||
258 | if (flag) { | |
259 | gpio_evmi->flags |= GPIOKPF_PRINT_PHANTOM_KEYS; | |
260 | } else { | |
261 | gpio_evmi->flags &= ~GPIOKPF_PRINT_PHANTOM_KEYS; | |
262 | } | |
263 | ||
264 | return count; | |
265 | } | |
266 | ||
267 | static ssize_t show_active_high_flag(struct device *dev, struct device_attribute *attr, char *buf) | |
268 | { | |
269 | if (!gpio_evmi) | |
270 | return -ENODEV; | |
271 | ||
272 | return snprintf(buf, PAGE_SIZE, "%u\n", (gpio_evmi->flags & GPIOKPF_ACTIVE_HIGH) ? 1 : 0); | |
273 | } | |
274 | ||
275 | static ssize_t store_active_high_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
276 | { | |
277 | unsigned flag; | |
278 | ||
279 | if (!gpio_evmi) | |
280 | return -ENODEV; | |
281 | ||
282 | sscanf(buf, "%u", &flag); | |
283 | ||
284 | if (flag) { | |
285 | gpio_evmi->flags |= GPIOKPF_ACTIVE_HIGH; | |
286 | } else { | |
287 | gpio_evmi->flags &= ~GPIOKPF_ACTIVE_HIGH; | |
288 | } | |
289 | ||
290 | return count; | |
291 | } | |
292 | ||
293 | static ssize_t show_drive_inactive_flag(struct device *dev, struct device_attribute *attr, char *buf) | |
294 | { | |
295 | if (!gpio_evmi) | |
296 | return -ENODEV; | |
297 | ||
298 | return snprintf(buf, PAGE_SIZE, "%u\n", (gpio_evmi->flags & GPIOKPF_DRIVE_INACTIVE) ? 1 : 0); | |
299 | } | |
300 | ||
301 | static ssize_t store_drive_inactive_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
302 | { | |
303 | unsigned flag; | |
304 | ||
305 | if (!gpio_evmi) | |
306 | return -ENODEV; | |
307 | ||
308 | sscanf(buf, "%u", &flag); | |
309 | ||
310 | if (flag) { | |
311 | gpio_evmi->flags |= GPIOKPF_DRIVE_INACTIVE; | |
312 | } else { | |
313 | gpio_evmi->flags &= ~GPIOKPF_DRIVE_INACTIVE; | |
314 | } | |
315 | ||
316 | return count; | |
317 | } | |
318 | ||
319 | static DEVICE_ATTR(debounce_delay, (S_IRUGO | S_IWUGO), show_debounce_delay, store_debounce_delay); | |
320 | static DEVICE_ATTR(settle_time, (S_IRUGO | S_IWUGO), show_settle_time, store_settle_time); | |
321 | static DEVICE_ATTR(poll_time, (S_IRUGO | S_IWUGO), show_poll_time, store_poll_time); | |
322 | static DEVICE_ATTR(flags, (S_IRUGO), show_flags, store_flags); | |
323 | static DEVICE_ATTR(debounce_flag, (S_IRUGO | S_IWUGO), show_debounce_flag, store_debounce_flag); | |
324 | static DEVICE_ATTR(remove_some_phantom_keys_flag, (S_IRUGO | S_IWUGO), show_remove_some_phantom_keys_flag, store_remove_some_phantom_keys_flag); | |
325 | static DEVICE_ATTR(print_unmapped_keys_flag, (S_IRUGO | S_IWUGO), show_print_unmapped_keys_flag, store_print_unmapped_keys_flag); | |
326 | static DEVICE_ATTR(print_mapped_keys_flag, (S_IRUGO | S_IWUGO), show_print_mapped_keys_flag, store_print_mapped_keys_flag); | |
327 | static DEVICE_ATTR(print_phantom_keys_flag, (S_IRUGO | S_IWUGO), show_print_phantom_keys_flag, store_print_phantom_keys_flag); | |
328 | static DEVICE_ATTR(active_high_flag, (S_IRUGO), show_active_high_flag, store_active_high_flag); | |
329 | static DEVICE_ATTR(drive_inactive_flag, (S_IRUGO | S_IWUGO), show_drive_inactive_flag, store_drive_inactive_flag); | |
330 | ||
331 | static void debounce_release(struct device *dev) | |
332 | { | |
333 | } | |
334 | ||
335 | static struct device debounce_device = { | |
336 | .init_name = "debounce", | |
337 | .release = debounce_release, | |
338 | }; | |
339 | ||
340 | static void hw_debounce_pin(int gpio, int enable) { | |
341 | printk(KERN_INFO PREFIX "%sabling hardware debounce for GPIO %d\n", (enable?"En":"Dis"), gpio); | |
342 | if (enable) | |
343 | omap_set_gpio_debounce_time(gpio, GPIO_DEBOUNCE_TIME); | |
344 | omap_set_gpio_debounce(gpio, enable); | |
345 | } | |
346 | ||
347 | static void hw_debounce(int enable) { | |
348 | int i; | |
349 | ||
350 | if (gpio_evmi == NULL) | |
351 | return; | |
352 | ||
353 | for (i = 0; i < gpio_evmi->ninputs; i++) { | |
354 | hw_debounce_pin(gpio_evmi->input_gpios[i], enable); | |
355 | } | |
356 | } | |
357 | ||
358 | static int __init debounce_init(void) | |
359 | { | |
360 | struct device *event_dev = NULL; | |
361 | struct gpio_event_platform_data *gpio_epd; | |
362 | struct gpio_event_info *gpio_ei; | |
363 | int err = 0; | |
364 | ||
365 | printk(KERN_INFO PREFIX "Searching for " GPIO_EVENT_DEV_NAME "...\n"); | |
366 | ||
367 | event_dev = device_find_child(&platform_bus, GPIO_EVENT_DEV_NAME, find_ms2_dev); | |
368 | if (event_dev == NULL) | |
369 | return -ENODEV; | |
370 | ||
371 | gpio_epd = (struct gpio_event_platform_data*)event_dev->platform_data; | |
372 | printk(KERN_INFO PREFIX "And there is a %s connected...\n", gpio_epd->name); | |
373 | if (strcmp(gpio_epd->name, "sholes-keypad")) | |
374 | return -ENODEV; | |
375 | ||
376 | gpio_ei = (struct gpio_event_info*)gpio_epd->info[0]; | |
377 | gpio_evmi = container_of(gpio_ei, struct gpio_event_matrix_info, info); | |
378 | ||
379 | err = device_register(&debounce_device); | |
380 | if (err) { | |
381 | return err; | |
382 | } | |
383 | ||
384 | hw_debounce(1); | |
385 | ||
386 | err = device_create_file(&debounce_device, &dev_attr_debounce_delay); | |
387 | err = device_create_file(&debounce_device, &dev_attr_settle_time); | |
388 | err = device_create_file(&debounce_device, &dev_attr_poll_time); | |
389 | err = device_create_file(&debounce_device, &dev_attr_flags); | |
390 | err = device_create_file(&debounce_device, &dev_attr_debounce_flag); | |
391 | err = device_create_file(&debounce_device, &dev_attr_remove_some_phantom_keys_flag); | |
392 | err = device_create_file(&debounce_device, &dev_attr_print_unmapped_keys_flag); | |
393 | err = device_create_file(&debounce_device, &dev_attr_print_mapped_keys_flag); | |
394 | err = device_create_file(&debounce_device, &dev_attr_print_phantom_keys_flag); | |
395 | err = device_create_file(&debounce_device, &dev_attr_active_high_flag); | |
396 | err = device_create_file(&debounce_device, &dev_attr_drive_inactive_flag); | |
397 | ||
398 | printk(KERN_INFO PREFIX "settle_time: %u\n", gpio_evmi->settle_time.tv.nsec); | |
399 | printk(KERN_INFO PREFIX "poll_time: %u\n", gpio_evmi->poll_time.tv.nsec); | |
400 | printk(KERN_INFO PREFIX "debounce_delay: %u\n", gpio_evmi->debounce_delay.tv.nsec); | |
401 | printk(KERN_INFO PREFIX "flags: 0x%x\n", gpio_evmi->flags); | |
402 | ||
403 | old_debounce_delay = gpio_evmi->debounce_delay; | |
404 | old_settle_time = gpio_evmi->settle_time; | |
405 | old_poll_time = gpio_evmi->poll_time; | |
406 | old_flags = gpio_evmi->flags; | |
407 | ||
408 | printk(KERN_INFO PREFIX "flags: 0x%x\n", gpio_evmi->flags); | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | static void __exit debounce_exit(void) | |
414 | { | |
415 | if (gpio_evmi) { | |
416 | if (gpio_evmi->debounce_delay.tv.nsec != old_debounce_delay.tv.nsec) { | |
417 | printk(KERN_INFO PREFIX "Restoring debounce_delay\n"); | |
418 | gpio_evmi->debounce_delay = old_debounce_delay; | |
419 | printk(KERN_INFO PREFIX "debounce_delay: %u\n", gpio_evmi->debounce_delay.tv.nsec); | |
420 | } | |
421 | if (gpio_evmi->flags != old_flags) { | |
422 | printk(KERN_INFO PREFIX "Restoring flags\n"); | |
423 | gpio_evmi->flags = old_flags; | |
424 | printk(KERN_INFO PREFIX "flags: 0x%x\n", gpio_evmi->flags); | |
425 | } | |
426 | gpio_evmi->settle_time = old_settle_time; | |
427 | gpio_evmi->poll_time = old_poll_time; | |
428 | } | |
429 | hw_debounce(0); | |
430 | device_remove_file(&debounce_device, &dev_attr_debounce_delay); | |
431 | device_remove_file(&debounce_device, &dev_attr_settle_time); | |
432 | device_remove_file(&debounce_device, &dev_attr_poll_time); | |
433 | device_remove_file(&debounce_device, &dev_attr_flags); | |
434 | device_remove_file(&debounce_device, &dev_attr_debounce_flag); | |
435 | device_remove_file(&debounce_device, &dev_attr_remove_some_phantom_keys_flag); | |
436 | device_remove_file(&debounce_device, &dev_attr_print_unmapped_keys_flag); | |
437 | device_remove_file(&debounce_device, &dev_attr_print_mapped_keys_flag); | |
438 | device_remove_file(&debounce_device, &dev_attr_print_phantom_keys_flag); | |
439 | device_remove_file(&debounce_device, &dev_attr_active_high_flag); | |
440 | device_remove_file(&debounce_device, &dev_attr_drive_inactive_flag); | |
441 | device_unregister(&debounce_device); | |
442 | } | |
443 | ||
444 | module_init(debounce_init); | |
445 | module_exit(debounce_exit); | |
446 | ||
447 | MODULE_LICENSE("GPL"); | |
448 | MODULE_AUTHOR("Michael Gernoth <michael@gernoth.net>"); |