aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/sysrq.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2010-03-22 01:31:26 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2010-04-14 02:26:02 -0400
commit97f5f0cd8cd0a05449cbb77d1e6f02e026875802 (patch)
treee101f38dfd7bf4e759e96617f69ceedd497608e3 /drivers/char/sysrq.c
parentb91c4be730668e801aa6a2ea95f467cd9a1e0983 (diff)
Input: implement SysRq as a separate input handler
Instead of keeping SysRq support inside of legacy keyboard driver split it out into a separate input handler (filter). This stops most SysRq input events from leaking into evdev clients (some events, such as first SysRq scancode - not keycode - event, are still leaked into both legacy keyboard and evdev). [martinez.javier@gmail.com: fix compile error when CONFIG_MAGIC_SYSRQ is not defined] Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/char/sysrq.c')
-rw-r--r--drivers/char/sysrq.c243
1 files changed, 213 insertions, 30 deletions
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index 59de2525d303..193f9c214946 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -1,7 +1,4 @@
1/* -*- linux-c -*- 1/*
2 *
3 * $Id: sysrq.c,v 1.15 1998/08/23 14:56:41 mj Exp $
4 *
5 * Linux Magic System Request Key Hacks 2 * Linux Magic System Request Key Hacks
6 * 3 *
7 * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> 4 * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
@@ -10,8 +7,13 @@
10 * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com> 7 * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
11 * overhauled to use key registration 8 * overhauled to use key registration
12 * based upon discusions in irc://irc.openprojects.net/#kernelnewbies 9 * based upon discusions in irc://irc.openprojects.net/#kernelnewbies
10 *
11 * Copyright (c) 2010 Dmitry Torokhov
12 * Input handler conversion
13 */ 13 */
14 14
15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16
15#include <linux/sched.h> 17#include <linux/sched.h>
16#include <linux/interrupt.h> 18#include <linux/interrupt.h>
17#include <linux/mm.h> 19#include <linux/mm.h>
@@ -39,33 +41,34 @@
39#include <linux/hrtimer.h> 41#include <linux/hrtimer.h>
40#include <linux/oom.h> 42#include <linux/oom.h>
41#include <linux/slab.h> 43#include <linux/slab.h>
44#include <linux/input.h>
42 45
43#include <asm/ptrace.h> 46#include <asm/ptrace.h>
44#include <asm/irq_regs.h> 47#include <asm/irq_regs.h>
45 48
46/* Whether we react on sysrq keys or just ignore them */ 49/* Whether we react on sysrq keys or just ignore them */
47int __read_mostly __sysrq_enabled = 1; 50static int __read_mostly sysrq_enabled = 1;
48 51static bool __read_mostly sysrq_always_enabled;
49static int __read_mostly sysrq_always_enabled;
50 52
51int sysrq_on(void) 53static bool sysrq_on(void)
52{ 54{
53 return __sysrq_enabled || sysrq_always_enabled; 55 return sysrq_enabled || sysrq_always_enabled;
54} 56}
55 57
56/* 58/*
57 * A value of 1 means 'all', other nonzero values are an op mask: 59 * A value of 1 means 'all', other nonzero values are an op mask:
58 */ 60 */
59static inline int sysrq_on_mask(int mask) 61static bool sysrq_on_mask(int mask)
60{ 62{
61 return sysrq_always_enabled || __sysrq_enabled == 1 || 63 return sysrq_always_enabled ||
62 (__sysrq_enabled & mask); 64 sysrq_enabled == 1 ||
65 (sysrq_enabled & mask);
63} 66}
64 67
65static int __init sysrq_always_enabled_setup(char *str) 68static int __init sysrq_always_enabled_setup(char *str)
66{ 69{
67 sysrq_always_enabled = 1; 70 sysrq_always_enabled = true;
68 printk(KERN_INFO "debug: sysrq always enabled.\n"); 71 pr_info("sysrq always enabled.\n");
69 72
70 return 1; 73 return 1;
71} 74}
@@ -76,6 +79,7 @@ __setup("sysrq_always_enabled", sysrq_always_enabled_setup);
76static void sysrq_handle_loglevel(int key, struct tty_struct *tty) 79static void sysrq_handle_loglevel(int key, struct tty_struct *tty)
77{ 80{
78 int i; 81 int i;
82
79 i = key - '0'; 83 i = key - '0';
80 console_loglevel = 7; 84 console_loglevel = 7;
81 printk("Loglevel set to %d\n", i); 85 printk("Loglevel set to %d\n", i);
@@ -101,7 +105,7 @@ static struct sysrq_key_op sysrq_SAK_op = {
101 .enable_mask = SYSRQ_ENABLE_KEYBOARD, 105 .enable_mask = SYSRQ_ENABLE_KEYBOARD,
102}; 106};
103#else 107#else
104#define sysrq_SAK_op (*(struct sysrq_key_op *)0) 108#define sysrq_SAK_op (*(struct sysrq_key_op *)NULL)
105#endif 109#endif
106 110
107#ifdef CONFIG_VT 111#ifdef CONFIG_VT
@@ -119,7 +123,7 @@ static struct sysrq_key_op sysrq_unraw_op = {
119 .enable_mask = SYSRQ_ENABLE_KEYBOARD, 123 .enable_mask = SYSRQ_ENABLE_KEYBOARD,
120}; 124};
121#else 125#else
122#define sysrq_unraw_op (*(struct sysrq_key_op *)0) 126#define sysrq_unraw_op (*(struct sysrq_key_op *)NULL)
123#endif /* CONFIG_VT */ 127#endif /* CONFIG_VT */
124 128
125static void sysrq_handle_crash(int key, struct tty_struct *tty) 129static void sysrq_handle_crash(int key, struct tty_struct *tty)
@@ -195,7 +199,7 @@ static struct sysrq_key_op sysrq_showlocks_op = {
195 .action_msg = "Show Locks Held", 199 .action_msg = "Show Locks Held",
196}; 200};
197#else 201#else
198#define sysrq_showlocks_op (*(struct sysrq_key_op *)0) 202#define sysrq_showlocks_op (*(struct sysrq_key_op *)NULL)
199#endif 203#endif
200 204
201#ifdef CONFIG_SMP 205#ifdef CONFIG_SMP
@@ -298,7 +302,7 @@ static struct sysrq_key_op sysrq_ftrace_dump_op = {
298 .enable_mask = SYSRQ_ENABLE_DUMP, 302 .enable_mask = SYSRQ_ENABLE_DUMP,
299}; 303};
300#else 304#else
301#define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)0) 305#define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)NULL)
302#endif 306#endif
303 307
304static void sysrq_handle_showmem(int key, struct tty_struct *tty) 308static void sysrq_handle_showmem(int key, struct tty_struct *tty)
@@ -477,6 +481,7 @@ struct sysrq_key_op *__sysrq_get_key_op(int key)
477 i = sysrq_key_table_key2index(key); 481 i = sysrq_key_table_key2index(key);
478 if (i != -1) 482 if (i != -1)
479 op_p = sysrq_key_table[i]; 483 op_p = sysrq_key_table[i];
484
480 return op_p; 485 return op_p;
481} 486}
482 487
@@ -488,11 +493,7 @@ static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p)
488 sysrq_key_table[i] = op_p; 493 sysrq_key_table[i] = op_p;
489} 494}
490 495
491/* 496static void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
492 * This is the non-locking version of handle_sysrq. It must/can only be called
493 * by sysrq key handlers, as they are inside of the lock
494 */
495void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
496{ 497{
497 struct sysrq_key_op *op_p; 498 struct sysrq_key_op *op_p;
498 int orig_log_level; 499 int orig_log_level;
@@ -544,10 +545,6 @@ void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
544 spin_unlock_irqrestore(&sysrq_key_table_lock, flags); 545 spin_unlock_irqrestore(&sysrq_key_table_lock, flags);
545} 546}
546 547
547/*
548 * This function is called by the keyboard handler when SysRq is pressed
549 * and any other keycode arrives.
550 */
551void handle_sysrq(int key, struct tty_struct *tty) 548void handle_sysrq(int key, struct tty_struct *tty)
552{ 549{
553 if (sysrq_on()) 550 if (sysrq_on())
@@ -555,10 +552,177 @@ void handle_sysrq(int key, struct tty_struct *tty)
555} 552}
556EXPORT_SYMBOL(handle_sysrq); 553EXPORT_SYMBOL(handle_sysrq);
557 554
555#ifdef CONFIG_INPUT
556
557/* Simple translation table for the SysRq keys */
558static const unsigned char sysrq_xlate[KEY_MAX + 1] =
559 "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */
560 "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */
561 "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */
562 "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */
563 "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */
564 "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
565 "\r\000/"; /* 0x60 - 0x6f */
566
567static bool sysrq_down;
568static int sysrq_alt_use;
569static int sysrq_alt;
570
571static bool sysrq_filter(struct input_handle *handle, unsigned int type,
572 unsigned int code, int value)
573{
574 if (type != EV_KEY)
575 goto out;
576
577 switch (code) {
578
579 case KEY_LEFTALT:
580 case KEY_RIGHTALT:
581 if (value)
582 sysrq_alt = code;
583 else if (sysrq_down && code == sysrq_alt_use)
584 sysrq_down = false;
585 break;
586
587 case KEY_SYSRQ:
588 if (value == 1 && sysrq_alt) {
589 sysrq_down = true;
590 sysrq_alt_use = sysrq_alt;
591 }
592 break;
593
594 default:
595 if (sysrq_down && value && value != 2)
596 __handle_sysrq(sysrq_xlate[code], NULL, 1);
597 break;
598 }
599
600out:
601 return sysrq_down;
602}
603
604static int sysrq_connect(struct input_handler *handler,
605 struct input_dev *dev,
606 const struct input_device_id *id)
607{
608 struct input_handle *handle;
609 int error;
610
611 sysrq_down = false;
612 sysrq_alt = 0;
613
614 handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
615 if (!handle)
616 return -ENOMEM;
617
618 handle->dev = dev;
619 handle->handler = handler;
620 handle->name = "sysrq";
621
622 error = input_register_handle(handle);
623 if (error) {
624 pr_err("Failed to register input sysrq handler, error %d\n",
625 error);
626 goto err_free;
627 }
628
629 error = input_open_device(handle);
630 if (error) {
631 pr_err("Failed to open input device, error %d\n", error);
632 goto err_unregister;
633 }
634
635 return 0;
636
637 err_unregister:
638 input_unregister_handle(handle);
639 err_free:
640 kfree(handle);
641 return error;
642}
643
644static void sysrq_disconnect(struct input_handle *handle)
645{
646 input_close_device(handle);
647 input_unregister_handle(handle);
648 kfree(handle);
649}
650
651/*
652 * We are matching on KEY_LEFTALT insteard of KEY_SYSRQ because not all
653 * keyboards have SysRq ikey predefined and so user may add it to keymap
654 * later, but we expect all such keyboards to have left alt.
655 */
656static const struct input_device_id sysrq_ids[] = {
657 {
658 .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
659 INPUT_DEVICE_ID_MATCH_KEYBIT,
660 .evbit = { BIT_MASK(EV_KEY) },
661 .keybit = { BIT_MASK(KEY_LEFTALT) },
662 },
663 { },
664};
665
666static struct input_handler sysrq_handler = {
667 .filter = sysrq_filter,
668 .connect = sysrq_connect,
669 .disconnect = sysrq_disconnect,
670 .name = "sysrq",
671 .id_table = sysrq_ids,
672};
673
674static bool sysrq_handler_registered;
675
676static inline void sysrq_register_handler(void)
677{
678 int error;
679
680 error = input_register_handler(&sysrq_handler);
681 if (error)
682 pr_err("Failed to register input handler, error %d", error);
683 else
684 sysrq_handler_registered = true;
685}
686
687static inline void sysrq_unregister_handler(void)
688{
689 if (sysrq_handler_registered) {
690 input_unregister_handler(&sysrq_handler);
691 sysrq_handler_registered = false;
692 }
693}
694
695#else
696
697static inline void sysrq_register_handler(void)
698{
699}
700
701static inline void sysrq_unregister_handler(void)
702{
703}
704
705#endif /* CONFIG_INPUT */
706
707int sysrq_toggle_support(int enable_mask)
708{
709 bool was_enabled = sysrq_on();
710
711 sysrq_enabled = enable_mask;
712
713 if (was_enabled != sysrq_on()) {
714 if (sysrq_on())
715 sysrq_register_handler();
716 else
717 sysrq_unregister_handler();
718 }
719
720 return 0;
721}
722
558static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, 723static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p,
559 struct sysrq_key_op *remove_op_p) 724 struct sysrq_key_op *remove_op_p)
560{ 725{
561
562 int retval; 726 int retval;
563 unsigned long flags; 727 unsigned long flags;
564 728
@@ -599,6 +763,7 @@ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
599 return -EFAULT; 763 return -EFAULT;
600 __handle_sysrq(c, NULL, 0); 764 __handle_sysrq(c, NULL, 0);
601 } 765 }
766
602 return count; 767 return count;
603} 768}
604 769
@@ -606,10 +771,28 @@ static const struct file_operations proc_sysrq_trigger_operations = {
606 .write = write_sysrq_trigger, 771 .write = write_sysrq_trigger,
607}; 772};
608 773
774static void sysrq_init_procfs(void)
775{
776 if (!proc_create("sysrq-trigger", S_IWUSR, NULL,
777 &proc_sysrq_trigger_operations))
778 pr_err("Failed to register proc interface\n");
779}
780
781#else
782
783static inline void sysrq_init_procfs(void)
784{
785}
786
787#endif /* CONFIG_PROC_FS */
788
609static int __init sysrq_init(void) 789static int __init sysrq_init(void)
610{ 790{
611 proc_create("sysrq-trigger", S_IWUSR, NULL, &proc_sysrq_trigger_operations); 791 sysrq_init_procfs();
792
793 if (sysrq_on())
794 sysrq_register_handler();
795
612 return 0; 796 return 0;
613} 797}
614module_init(sysrq_init); 798module_init(sysrq_init);
615#endif