diff options
author | Mathieu J. Poirier <mathieu.poirier@linaro.org> | 2013-04-02 01:14:19 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-04-02 01:22:19 -0400 |
commit | 3903078677a8dc6f056970b67d15840aa51e1dfa (patch) | |
tree | f6bbe33c95c1768a3e8cc474c8be43a9b11b40a4 /drivers/tty | |
parent | e10af9e79a97ac5d305ec1be94338e8d3fd7aaa4 (diff) |
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>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/sysrq.c | 53 |
1 files changed, 40 insertions, 13 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 3687f0cad642..b6b267d939a0 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <linux/input.h> | 43 | #include <linux/input.h> |
44 | #include <linux/uaccess.h> | 44 | #include <linux/uaccess.h> |
45 | #include <linux/moduleparam.h> | 45 | #include <linux/moduleparam.h> |
46 | #include <linux/jiffies.h> | ||
46 | 47 | ||
47 | #include <asm/ptrace.h> | 48 | #include <asm/ptrace.h> |
48 | #include <asm/irq_regs.h> | 49 | #include <asm/irq_regs.h> |
@@ -51,6 +52,9 @@ | |||
51 | static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE; | 52 | static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE; |
52 | static bool __read_mostly sysrq_always_enabled; | 53 | static bool __read_mostly sysrq_always_enabled; |
53 | 54 | ||
55 | unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED }; | ||
56 | int sysrq_reset_downtime_ms __weak; | ||
57 | |||
54 | static bool sysrq_on(void) | 58 | static bool sysrq_on(void) |
55 | { | 59 | { |
56 | return sysrq_enabled || sysrq_always_enabled; | 60 | return sysrq_enabled || sysrq_always_enabled; |
@@ -586,6 +590,7 @@ struct sysrq_state { | |||
586 | int reset_seq_len; | 590 | int reset_seq_len; |
587 | int reset_seq_cnt; | 591 | int reset_seq_cnt; |
588 | int reset_seq_version; | 592 | int reset_seq_version; |
593 | struct timer_list keyreset_timer; | ||
589 | }; | 594 | }; |
590 | 595 | ||
591 | #define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ | 596 | #define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ |
@@ -619,29 +624,51 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state) | |||
619 | state->reset_seq_version = sysrq_reset_seq_version; | 624 | state->reset_seq_version = sysrq_reset_seq_version; |
620 | } | 625 | } |
621 | 626 | ||
622 | static bool sysrq_detect_reset_sequence(struct sysrq_state *state, | 627 | static void sysrq_do_reset(unsigned long dummy) |
628 | { | ||
629 | __handle_sysrq(sysrq_xlate[KEY_B], false); | ||
630 | } | ||
631 | |||
632 | static void sysrq_handle_reset_request(struct sysrq_state *state) | ||
633 | { | ||
634 | if (sysrq_reset_downtime_ms) | ||
635 | mod_timer(&state->keyreset_timer, | ||
636 | jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms)); | ||
637 | else | ||
638 | sysrq_do_reset(0); | ||
639 | } | ||
640 | |||
641 | static void sysrq_detect_reset_sequence(struct sysrq_state *state, | ||
623 | unsigned int code, int value) | 642 | unsigned int code, int value) |
624 | { | 643 | { |
625 | if (!test_bit(code, state->reset_keybit)) { | 644 | if (!test_bit(code, state->reset_keybit)) { |
626 | /* | 645 | /* |
627 | * Pressing any key _not_ in reset sequence cancels | 646 | * Pressing any key _not_ in reset sequence cancels |
628 | * the reset sequence. | 647 | * the reset sequence. Also cancelling the timer in |
648 | * case additional keys were pressed after a reset | ||
649 | * has been requested. | ||
629 | */ | 650 | */ |
630 | if (value && state->reset_seq_cnt) | 651 | if (value && state->reset_seq_cnt) { |
631 | state->reset_canceled = true; | 652 | state->reset_canceled = true; |
653 | del_timer(&state->keyreset_timer); | ||
654 | } | ||
632 | } else if (value == 0) { | 655 | } else if (value == 0) { |
633 | /* key release */ | 656 | /* |
657 | * Key release - all keys in the reset sequence need | ||
658 | * to be pressed and held for the reset timeout | ||
659 | * to hold. | ||
660 | */ | ||
661 | del_timer(&state->keyreset_timer); | ||
662 | |||
634 | if (--state->reset_seq_cnt == 0) | 663 | if (--state->reset_seq_cnt == 0) |
635 | state->reset_canceled = false; | 664 | state->reset_canceled = false; |
636 | } else if (value == 1) { | 665 | } else if (value == 1) { |
637 | /* key press, not autorepeat */ | 666 | /* key press, not autorepeat */ |
638 | if (++state->reset_seq_cnt == state->reset_seq_len && | 667 | if (++state->reset_seq_cnt == state->reset_seq_len && |
639 | !state->reset_canceled) { | 668 | !state->reset_canceled) { |
640 | return true; | 669 | sysrq_handle_reset_request(state); |
641 | } | 670 | } |
642 | } | 671 | } |
643 | |||
644 | return false; | ||
645 | } | 672 | } |
646 | 673 | ||
647 | static void sysrq_reinject_alt_sysrq(struct work_struct *work) | 674 | static void sysrq_reinject_alt_sysrq(struct work_struct *work) |
@@ -748,10 +775,8 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq, | |||
748 | if (was_active) | 775 | if (was_active) |
749 | schedule_work(&sysrq->reinject_work); | 776 | schedule_work(&sysrq->reinject_work); |
750 | 777 | ||
751 | if (sysrq_detect_reset_sequence(sysrq, code, value)) { | 778 | /* Check for reset sequence */ |
752 | /* Force emergency reboot */ | 779 | sysrq_detect_reset_sequence(sysrq, code, value); |
753 | __handle_sysrq(sysrq_xlate[KEY_B], false); | ||
754 | } | ||
755 | 780 | ||
756 | } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { | 781 | } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { |
757 | /* | 782 | /* |
@@ -812,6 +837,7 @@ static int sysrq_connect(struct input_handler *handler, | |||
812 | sysrq->handle.handler = handler; | 837 | sysrq->handle.handler = handler; |
813 | sysrq->handle.name = "sysrq"; | 838 | sysrq->handle.name = "sysrq"; |
814 | sysrq->handle.private = sysrq; | 839 | sysrq->handle.private = sysrq; |
840 | setup_timer(&sysrq->keyreset_timer, sysrq_do_reset, 0); | ||
815 | 841 | ||
816 | error = input_register_handle(&sysrq->handle); | 842 | error = input_register_handle(&sysrq->handle); |
817 | if (error) { | 843 | if (error) { |
@@ -841,6 +867,7 @@ static void sysrq_disconnect(struct input_handle *handle) | |||
841 | 867 | ||
842 | input_close_device(handle); | 868 | input_close_device(handle); |
843 | cancel_work_sync(&sysrq->reinject_work); | 869 | cancel_work_sync(&sysrq->reinject_work); |
870 | del_timer_sync(&sysrq->keyreset_timer); | ||
844 | input_unregister_handle(handle); | 871 | input_unregister_handle(handle); |
845 | kfree(sysrq); | 872 | kfree(sysrq); |
846 | } | 873 | } |
@@ -870,8 +897,6 @@ static struct input_handler sysrq_handler = { | |||
870 | 897 | ||
871 | static bool sysrq_handler_registered; | 898 | static bool sysrq_handler_registered; |
872 | 899 | ||
873 | unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED }; | ||
874 | |||
875 | static inline void sysrq_register_handler(void) | 900 | static inline void sysrq_register_handler(void) |
876 | { | 901 | { |
877 | unsigned short key; | 902 | unsigned short key; |
@@ -931,6 +956,8 @@ static struct kernel_param_ops param_ops_sysrq_reset_seq = { | |||
931 | module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, | 956 | module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, |
932 | &sysrq_reset_seq_len, 0644); | 957 | &sysrq_reset_seq_len, 0644); |
933 | 958 | ||
959 | module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644); | ||
960 | |||
934 | #else | 961 | #else |
935 | 962 | ||
936 | static inline void sysrq_register_handler(void) | 963 | static inline void sysrq_register_handler(void) |