input: keyreset: add support for reset after timeout
If a timeout is requested, a delayed work item will be
scheduled to restart the device. If the keys are released
before the timeout expires, the reset is aborted.
As expected, the reset_fn can be used to reset the device
after the timeout.
Bug: 7344361
Change-Id: I1d77cdb3dcc63f579b1250506f0a30de1e033d67
Signed-off-by: Dima Zavin <dima@android.com>
diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c
index 44e26dc..5b6c73b 100644
--- a/drivers/input/keyreset.c
+++ b/drivers/input/keyreset.c
@@ -21,6 +21,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
+#include <linux/workqueue.h>
struct keyreset_state {
@@ -33,18 +34,28 @@
int key_down;
int key_up;
int restart_disabled;
+ int restart_requested;
int (*reset_fn)(void);
+ int down_time_ms;
+ struct delayed_work restart_work;
};
-int restart_requested;
-static void deferred_restart(struct work_struct *dummy)
+static void deferred_restart(struct work_struct *work)
{
- restart_requested = 2;
- sys_sync();
- restart_requested = 3;
- kernel_restart(NULL);
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct keyreset_state *state =
+ container_of(dwork, struct keyreset_state, restart_work);
+
+ pr_info("keyreset: restarting system\n");
+ if (state->reset_fn) {
+ state->restart_requested = state->reset_fn();
+ } else {
+ state->restart_requested = 2;
+ sys_sync();
+ state->restart_requested = 3;
+ kernel_restart(NULL);
+ }
}
-static DECLARE_WORK(restart_work, deferred_restart);
static void keyreset_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
@@ -77,8 +88,16 @@
else
state->key_down--;
}
- if (state->key_down == 0 && state->key_up == 0)
+ if (state->key_down == 0 && state->key_up == 0) {
state->restart_disabled = 0;
+ if (state->down_time_ms) {
+ __cancel_delayed_work(&state->restart_work);
+ if (state->restart_requested) {
+ pr_info("keyboard reset canceled\n");
+ state->restart_requested = 0;
+ }
+ }
+ }
pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value,
state->key_down, state->key_up, state->restart_disabled);
@@ -86,14 +105,17 @@
if (value && !state->restart_disabled &&
state->key_down == state->key_down_target) {
state->restart_disabled = 1;
- if (restart_requested)
- panic("keyboard reset failed, %d", restart_requested);
- if (state->reset_fn) {
- restart_requested = state->reset_fn();
+ if (state->restart_requested)
+ panic("keyboard reset failed, %d",
+ state->restart_requested);
+ if (state->reset_fn && state->down_time_ms == 0) {
+ state->restart_requested = state->reset_fn();
} else {
- pr_info("keyboard reset\n");
- schedule_work(&restart_work);
- restart_requested = 1;
+ pr_info("keyboard reset (delayed %dms)\n",
+ state->down_time_ms);
+ schedule_delayed_work(&state->restart_work,
+ msecs_to_jiffies(state->down_time_ms));
+ state->restart_requested = 1;
}
}
done:
@@ -203,6 +225,11 @@
if (pdata->reset_fn)
state->reset_fn = pdata->reset_fn;
+ if (pdata->down_time_ms)
+ state->down_time_ms = pdata->down_time_ms;
+
+ INIT_DELAYED_WORK(&state->restart_work, deferred_restart);
+
state->input_handler.event = keyreset_event;
state->input_handler.connect = keyreset_connect;
state->input_handler.disconnect = keyreset_disconnect;
@@ -221,6 +248,7 @@
{
struct keyreset_state *state = platform_get_drvdata(pdev);
input_unregister_handler(&state->input_handler);
+ cancel_delayed_work_sync(&state->restart_work);
kfree(state);
return 0;
}
diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h
index a2ac49e..22ebe11 100644
--- a/include/linux/keyreset.h
+++ b/include/linux/keyreset.h
@@ -21,6 +21,7 @@
struct keyreset_platform_data {
int (*reset_fn)(void);
+ int down_time_ms;
int *keys_up;
int keys_down[]; /* 0 terminated */
};