Skip to content

Commit

Permalink
Input: sysrq - supplement reset sequence with timeout functionality
Browse files Browse the repository at this point in the history
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: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
  • Loading branch information
Mathieu J. Poirier authored and Dmitry Torokhov committed Apr 2, 2013
1 parent e10af9e commit 3903078
Showing 1 changed file with 40 additions and 13 deletions.
53 changes: 40 additions & 13 deletions drivers/tty/sysrq.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/moduleparam.h>
#include <linux/jiffies.h>

#include <asm/ptrace.h>
#include <asm/irq_regs.h>
Expand All @@ -51,6 +52,9 @@
static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
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)
{
return sysrq_enabled || sysrq_always_enabled;
Expand Down Expand Up @@ -586,6 +590,7 @@ struct sysrq_state {
int reset_seq_len;
int reset_seq_cnt;
int reset_seq_version;
struct timer_list keyreset_timer;
};

#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */
Expand Down Expand Up @@ -619,29 +624,51 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state)
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)
{
if (!test_bit(code, state->reset_keybit)) {
/*
* 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;
del_timer(&state->keyreset_timer);
}
} 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)
state->reset_canceled = false;
} else if (value == 1) {
/* key press, not autorepeat */
if (++state->reset_seq_cnt == state->reset_seq_len &&
!state->reset_canceled) {
return true;
sysrq_handle_reset_request(state);
}
}

return false;
}

static void sysrq_reinject_alt_sysrq(struct work_struct *work)
Expand Down Expand Up @@ -748,10 +775,8 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
if (was_active)
schedule_work(&sysrq->reinject_work);

if (sysrq_detect_reset_sequence(sysrq, code, value)) {
/* Force emergency reboot */
__handle_sysrq(sysrq_xlate[KEY_B], false);
}
/* Check for reset sequence */
sysrq_detect_reset_sequence(sysrq, code, value);

} else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
/*
Expand Down Expand Up @@ -812,6 +837,7 @@ static int sysrq_connect(struct input_handler *handler,
sysrq->handle.handler = handler;
sysrq->handle.name = "sysrq";
sysrq->handle.private = sysrq;
setup_timer(&sysrq->keyreset_timer, sysrq_do_reset, 0);

error = input_register_handle(&sysrq->handle);
if (error) {
Expand Down Expand Up @@ -841,6 +867,7 @@ static void sysrq_disconnect(struct input_handle *handle)

input_close_device(handle);
cancel_work_sync(&sysrq->reinject_work);
del_timer_sync(&sysrq->keyreset_timer);
input_unregister_handle(handle);
kfree(sysrq);
}
Expand Down Expand Up @@ -870,8 +897,6 @@ static struct input_handler sysrq_handler = {

static bool sysrq_handler_registered;

unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };

static inline void sysrq_register_handler(void)
{
unsigned short key;
Expand Down Expand Up @@ -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,
&sysrq_reset_seq_len, 0644);

module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644);

#else

static inline void sysrq_register_handler(void)
Expand Down

0 comments on commit 3903078

Please sign in to comment.