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/

Reply via email to