Commit 39030786 authored by Mathieu J. Poirier's avatar Mathieu J. Poirier Committed by Dmitry Torokhov

Input: sysrq - supplement reset sequence with timeout functionality

Some devices have too few buttons, which it makes it hard to have
a reset combo that won't trigger automatically.  As such a
timeout functionality that requires the combination to be held for
a given amount of time before triggering is introduced.

If a key combo is recognized and held for a 'timeout' amount of time,
the system triggers a reset.  If the timeout value is omitted the
driver simply ignores the functionality.
Signed-off-by: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent e10af9e7
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/jiffies.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/irq_regs.h> #include <asm/irq_regs.h>
...@@ -51,6 +52,9 @@ ...@@ -51,6 +52,9 @@
static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE; static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
static bool __read_mostly sysrq_always_enabled; static bool __read_mostly sysrq_always_enabled;
unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
int sysrq_reset_downtime_ms __weak;
static bool sysrq_on(void) static bool sysrq_on(void)
{ {
return sysrq_enabled || sysrq_always_enabled; return sysrq_enabled || sysrq_always_enabled;
...@@ -586,6 +590,7 @@ struct sysrq_state { ...@@ -586,6 +590,7 @@ struct sysrq_state {
int reset_seq_len; int reset_seq_len;
int reset_seq_cnt; int reset_seq_cnt;
int reset_seq_version; int reset_seq_version;
struct timer_list keyreset_timer;
}; };
#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ #define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */
...@@ -619,29 +624,51 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state) ...@@ -619,29 +624,51 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state)
state->reset_seq_version = sysrq_reset_seq_version; state->reset_seq_version = sysrq_reset_seq_version;
} }
static bool sysrq_detect_reset_sequence(struct sysrq_state *state, static void sysrq_do_reset(unsigned long dummy)
{
__handle_sysrq(sysrq_xlate[KEY_B], false);
}
static void sysrq_handle_reset_request(struct sysrq_state *state)
{
if (sysrq_reset_downtime_ms)
mod_timer(&state->keyreset_timer,
jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms));
else
sysrq_do_reset(0);
}
static void sysrq_detect_reset_sequence(struct sysrq_state *state,
unsigned int code, int value) unsigned int code, int value)
{ {
if (!test_bit(code, state->reset_keybit)) { if (!test_bit(code, state->reset_keybit)) {
/* /*
* Pressing any key _not_ in reset sequence cancels * Pressing any key _not_ in reset sequence cancels
* the reset sequence. * the reset sequence. Also cancelling the timer in
* case additional keys were pressed after a reset
* has been requested.
*/ */
if (value && state->reset_seq_cnt) if (value && state->reset_seq_cnt) {
state->reset_canceled = true; state->reset_canceled = true;
del_timer(&state->keyreset_timer);
}
} else if (value == 0) { } else if (value == 0) {
/* key release */ /*
* Key release - all keys in the reset sequence need
* to be pressed and held for the reset timeout
* to hold.
*/
del_timer(&state->keyreset_timer);
if (--state->reset_seq_cnt == 0) if (--state->reset_seq_cnt == 0)
state->reset_canceled = false; state->reset_canceled = false;
} else if (value == 1) { } else if (value == 1) {
/* key press, not autorepeat */ /* key press, not autorepeat */
if (++state->reset_seq_cnt == state->reset_seq_len && if (++state->reset_seq_cnt == state->reset_seq_len &&
!state->reset_canceled) { !state->reset_canceled) {
return true; sysrq_handle_reset_request(state);
} }
} }
return false;
} }
static void sysrq_reinject_alt_sysrq(struct work_struct *work) static void sysrq_reinject_alt_sysrq(struct work_struct *work)
...@@ -748,10 +775,8 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq, ...@@ -748,10 +775,8 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
if (was_active) if (was_active)
schedule_work(&sysrq->reinject_work); schedule_work(&sysrq->reinject_work);
if (sysrq_detect_reset_sequence(sysrq, code, value)) { /* Check for reset sequence */
/* Force emergency reboot */ sysrq_detect_reset_sequence(sysrq, code, value);
__handle_sysrq(sysrq_xlate[KEY_B], false);
}
} else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
/* /*
...@@ -812,6 +837,7 @@ static int sysrq_connect(struct input_handler *handler, ...@@ -812,6 +837,7 @@ static int sysrq_connect(struct input_handler *handler,
sysrq->handle.handler = handler; sysrq->handle.handler = handler;
sysrq->handle.name = "sysrq"; sysrq->handle.name = "sysrq";
sysrq->handle.private = sysrq; sysrq->handle.private = sysrq;
setup_timer(&sysrq->keyreset_timer, sysrq_do_reset, 0);
error = input_register_handle(&sysrq->handle); error = input_register_handle(&sysrq->handle);
if (error) { if (error) {
...@@ -841,6 +867,7 @@ static void sysrq_disconnect(struct input_handle *handle) ...@@ -841,6 +867,7 @@ static void sysrq_disconnect(struct input_handle *handle)
input_close_device(handle); input_close_device(handle);
cancel_work_sync(&sysrq->reinject_work); cancel_work_sync(&sysrq->reinject_work);
del_timer_sync(&sysrq->keyreset_timer);
input_unregister_handle(handle); input_unregister_handle(handle);
kfree(sysrq); kfree(sysrq);
} }
...@@ -870,8 +897,6 @@ static struct input_handler sysrq_handler = { ...@@ -870,8 +897,6 @@ static struct input_handler sysrq_handler = {
static bool sysrq_handler_registered; static bool sysrq_handler_registered;
unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
static inline void sysrq_register_handler(void) static inline void sysrq_register_handler(void)
{ {
unsigned short key; unsigned short key;
...@@ -931,6 +956,8 @@ static struct kernel_param_ops param_ops_sysrq_reset_seq = { ...@@ -931,6 +956,8 @@ static struct kernel_param_ops param_ops_sysrq_reset_seq = {
module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
&sysrq_reset_seq_len, 0644); &sysrq_reset_seq_len, 0644);
module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644);
#else #else
static inline void sysrq_register_handler(void) static inline void sysrq_register_handler(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment