diff options
Diffstat (limited to 'drivers/input/keyboard/input-hook.c')
| -rw-r--r-- | drivers/input/keyboard/input-hook.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/drivers/input/keyboard/input-hook.c b/drivers/input/keyboard/input-hook.c new file mode 100644 index 00000000..cf55c3b0 --- /dev/null +++ b/drivers/input/keyboard/input-hook.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2013 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/input-hook.h> + +static enum hrtimer_restart trigger_watch_event_timer_cb(struct hrtimer *timer) +{ + struct trigger_watch_event *event = container_of(timer, struct trigger_watch_event, timer); + enum hrtimer_restart restart = HRTIMER_NORESTART; + pr_info("< %s > trigger-watch event timer timeout %dms\n", event->name, event->period); + if (event->trigger_watch_event_cb) + event->trigger_watch_event_cb(event->private); + if (event->repeated) { /* repeat timer */ + hrtimer_forward_now(timer, ktime_set(event->period / 1000, (event->period % 1000) * 1000000)); + restart = HRTIMER_RESTART; + } + return restart; +} + +int trigger_watch_event_stop(struct trigger_watch_event *event) +{ + if (!event->disable_timer) { + hrtimer_cancel(&event->timer); + } + if (!event->disable_clk_source) { + u64 delta = (local_clock() - event->last_ts); + if (delta > ((u64)event->period) * 1000000) { + pr_info("< %s > trigger-watch event clock timeout %d / %lld\n", event->name, event->period, delta); + if (event->trigger_watch_event_cb) + event->trigger_watch_event_cb(event->private); + } + } + return 0; +} +EXPORT_SYMBOL(trigger_watch_event_stop); + +void trigger_watch_event_start(struct trigger_watch_event *event, int ms) +{ + if (!event->initialized) { + event->initialized = 1; + if (!event->disable_timer) { + hrtimer_init(&event->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + event->timer.function = trigger_watch_event_timer_cb; + } + } + event->repeated = !!(ms & TRIGGER_WATCH_EVENT_REPEATED); + event->period = (ms & ~TRIGGER_WATCH_EVENT_REPEATED); + event->last_ts = local_clock(); + trigger_watch_event_stop(event); + if (!event->disable_timer) { + hrtimer_start(&event->timer, ktime_set(event->period / 1000, (event->period % 1000) * 1000000), HRTIMER_MODE_REL); + } +} +EXPORT_SYMBOL(trigger_watch_event_start); + +static int hook_power = 1; + +static struct ctl_table_header *hook_power_sysctl_hdr = NULL; +void input_hook_init(void) +{ + static ctl_table hook_power_sysctl_table[] = { + { + .procname = "hook_power_enable", + .data = &hook_power, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + {} + }; + + static ctl_table hook_power_sysctl_root[] = { + { + .procname = "kernel", + .mode = 0555, + .child = hook_power_sysctl_table, + }, + {} + }; + + hook_power_sysctl_hdr = register_sysctl_table(hook_power_sysctl_root); + if (!hook_power_sysctl_hdr) { + pr_err("unable to create input_power entry\n"); + return -ENOMEM; + } + return 0; +} + +void input_hook_exit(void) +{ + if (hook_power_sysctl_hdr) + unregister_sysctl_table(hook_power_sysctl_hdr); +} + +extern void sprd_pbint_7s_reset_disable(void); +static void trigger_watch_powerkey(void *private) +{ + unsigned long flags; + local_irq_save(flags); +#ifdef CONFIG_MAGIC_SYSRQ + handle_sysrq('m'); + handle_sysrq('w'); +#endif + sprd_pbint_7s_reset_disable(); + pr_warn("!!!! trigger_powerkey !!!! do panic\n"); + panic("!!!! trigger_powerkey !!!! do panic"); + pr_err("%s should never reach here!\n", __func__); +} + +void input_report_key_hook(struct input_dev *dev, unsigned int code, int value) +{ + + if (hook_power && code == KEY_POWER) { + static struct trigger_watch_event twe = { + .name = "power key", + .private = NULL, /* like dev */ + .trigger_watch_event_cb = trigger_watch_powerkey, + }; + static int pre_value = 0; + if (value != pre_value) { + if (value) + trigger_watch_event_start(&twe, 6 * 1000); + else + trigger_watch_event_stop(&twe); + } else { + pr_warn("Key %d%c is dithering.", code, value ? 'D' : 'U'); + } + pre_value = value; + } +} |
