Hi Mathieu, On Sun, Nov 11, 2012 at 01:24:48PM -0700, mathieu.poir...@linaro.org wrote: > From: "Mathieu J. Poirier" <mathieu.poir...@linaro.org> > > This patch adds keyreset functionality to the sysrq driver. It > allows certain button/key combinations to be used in order to > trigger device resets. > > The first time the key-combo is detected a work function that syncs > the filesystems is scheduled and the kernel rebooted. If all the keys > are released and then pressed again, it calls panic. Reboot on panic > should be set for this to work. > > Redefining the '__weak sysrq_keyreset_get_params' function is required > to trigger the feature. Alternatively keys can be passed to the > driver via the "/sys/module/sysrq" interface. > > This functionality comes from the keyreset driver submitted by > Arve Hjønnevåg in the Android kernel.
Thank you for making the changes. This still looks pretty complicated, how about if we trim it a bit, like in the patch below. Thanks. -- Dmitry Input: sysrq - allow specifying alternate reset sequence From: Mathieu J. Poirier <mathieu.poir...@linaro.org> This patch adds keyreset functionality to the sysrq driver. It allows certain button/key combinations to be used in order to trigger emergency reboots. Redefining the '__weak platform_sysrq_reset_seq' variable is required to trigger the feature. Alternatively keys can be passed to the driver via a module parameter. This functionality comes from the keyreset driver submitted by Arve Hjønnevåg in the Android kernel. Signed-off-by: Mathieu Poirier <mathieu.poir...@linaro.org> Signed-off-by: Dmitry Torokhov <dmitry.torok...@gmail.com> --- drivers/tty/sysrq.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 16ee6ce..9dddaf7 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -41,6 +41,7 @@ #include <linux/slab.h> #include <linux/input.h> #include <linux/uaccess.h> +#include <linux/moduleparam.h> #include <asm/ptrace.h> #include <asm/irq_regs.h> @@ -576,8 +577,73 @@ struct sysrq_state { bool active; bool need_reinject; bool reinjecting; + + /* reset sequence handling */ + bool reset_canceled; + unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)]; + int reset_seq_len; + int reset_seq_cnt; + int reset_seq_version; }; +#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ +static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX]; +static unsigned int sysrq_reset_seq_len; +static unsigned int sysrq_reset_seq_version = 1; + +static void sysrq_parse_reset_sequence(struct sysrq_state *state) +{ + int i; + unsigned short key; + + state->reset_seq_cnt = 0; + + for (i = 0; i < sysrq_reset_seq_len; i++) { + key = sysrq_reset_seq[i]; + + if (key == KEY_RESERVED || key > KEY_MAX) + break; + + __set_bit(key, state->reset_keybit); + state->reset_seq_len++; + + if (test_bit(key, state->key_down)) + state->reset_seq_cnt++; + } +} + +static bool sysrq_detect_reset_sequence(struct sysrq_state *state, + unsigned int code, int value) +{ + if (state->reset_seq_version != sysrq_reset_seq_version) { + sysrq_parse_reset_sequence(state); + /* Disable reset until old keys are not released */ + state->reset_canceled = state->reset_seq_cnt != 0; + state->reset_seq_version = sysrq_reset_seq_version; + } + + if (!test_bit(code, state->reset_keybit)) { + /* + * Pressing any key _not_ in reset sequence cancels + * the reset sequence. + */ + if (value && state->reset_seq_cnt) + state->reset_canceled = true; + } else if (value == 0) { + /* key release */ + 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; + } + } + + return false; +} + static void sysrq_reinject_alt_sysrq(struct work_struct *work) { struct sysrq_state *sysrq = @@ -690,6 +756,11 @@ static bool sysrq_filter(struct input_handle *handle, 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); + } + } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { /* @@ -785,7 +856,20 @@ static bool sysrq_handler_registered; static inline void sysrq_register_handler(void) { + extern unsigned short platform_sysrq_reset_seq[] __weak; + unsigned short key; int error; + int i; + + if (platform_sysrq_reset_seq) { + for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) { + key = platform_sysrq_reset_seq[i]; + if (key == KEY_RESERVED || key > KEY_MAX) + break; + + sysrq_reset_seq[sysrq_reset_seq_len++] = key; + } + } error = input_register_handler(&sysrq_handler); if (error) @@ -802,6 +886,36 @@ static inline void sysrq_unregister_handler(void) } } +static int sysrq_reset_seq_param_set(const char *buffer, + const struct kernel_param *kp) +{ + unsigned long val; + int error; + + error = strict_strtoul(buffer, 0, &val); + if (error < 0) + return error; + + if (val > KEY_MAX) + return -EINVAL; + + *((unsigned short *)kp->arg) = val; + sysrq_reset_seq_version++; + + return 0; +} + +static struct kernel_param_ops param_ops_sysrq_reset_seq = { + .get = param_get_ushort, + .set = sysrq_reset_seq_param_set, +}; + +#define param_check_sysrq_reset_seq(name, p) \ + __param_check(name, p, unsigned short) + +module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, + &sysrq_reset_seq_len, 0644); + #else static inline void sysrq_register_handler(void) -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/