Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+40 −13
Original line number Diff line number Diff line
@@ -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)