mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-15 09:34:17 +00:00
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: Mathieu Poirier <mathieu.poirier@linaro.org> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
parent
e10af9e79a
commit
3903078677
@ -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>
|
||||
@ -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;
|
||||
@ -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 */
|
||||
@ -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)
|
||||
@ -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)) {
|
||||
/*
|
||||
@ -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) {
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user