aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorMathieu J. Poirier <mathieu.poirier@linaro.org>2013-04-02 01:14:19 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2013-04-02 01:22:19 -0400
commit3903078677a8dc6f056970b67d15840aa51e1dfa (patch)
treef6bbe33c95c1768a3e8cc474c8be43a9b11b40a4 /drivers/tty
parente10af9e79a97ac5d305ec1be94338e8d3fd7aaa4 (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.c53
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 @@
51static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE; 52static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE;
52static bool __read_mostly sysrq_always_enabled; 53static bool __read_mostly sysrq_always_enabled;
53 54
55unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
56int sysrq_reset_downtime_ms __weak;
57
54static bool sysrq_on(void) 58static 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
622static bool sysrq_detect_reset_sequence(struct sysrq_state *state, 627static void sysrq_do_reset(unsigned long dummy)
628{
629 __handle_sysrq(sysrq_xlate[KEY_B], false);
630}
631
632static 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
641static 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
647static void sysrq_reinject_alt_sysrq(struct work_struct *work) 674static 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
871static bool sysrq_handler_registered; 898static bool sysrq_handler_registered;
872 899
873unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED };
874
875static inline void sysrq_register_handler(void) 900static 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 = {
931module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, 956module_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
959module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644);
960
934#else 961#else
935 962
936static inline void sysrq_register_handler(void) 963static inline void sysrq_register_handler(void)