aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/sysrq.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2010-11-15 03:23:42 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2010-11-15 04:26:33 -0500
commitfcb7193096969ca9e5b9219b9a99ddf299d4054f (patch)
treea9d06c889c85102f0f9baafbeea60aabde8f1acc /drivers/char/sysrq.c
parent5fdbe44d033d059cc56c2803e6b4dbd8cb4e5e39 (diff)
Input: sysrq - pass along lone Alt + SysRq
When user presses and releases Alt + SysRq without pressing any of the hot keys re-inject the combination and pass it on to userspace instead of suppressing it - maybe he or she wanted to take print screen instead of invoking SysRq handler. Also pass along release events for keys that have been pressed before SysRq mode has been invoked so that keys do not appear to be "stuck". Acked-by: Jason Wessel <jason.wessel@windriver.com> Tested-by: Jason Wessel <jason.wessel@windriver.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/char/sysrq.c')
-rw-r--r--drivers/char/sysrq.c169
1 files changed, 124 insertions, 45 deletions
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index eaa5d3efa79d..c556ed9db13d 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -554,7 +554,7 @@ EXPORT_SYMBOL(handle_sysrq);
554#ifdef CONFIG_INPUT 554#ifdef CONFIG_INPUT
555 555
556/* Simple translation table for the SysRq keys */ 556/* Simple translation table for the SysRq keys */
557static const unsigned char sysrq_xlate[KEY_MAX + 1] = 557static const unsigned char sysrq_xlate[KEY_CNT] =
558 "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ 558 "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */
559 "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ 559 "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */
560 "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ 560 "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */
@@ -563,53 +563,129 @@ static const unsigned char sysrq_xlate[KEY_MAX + 1] =
563 "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ 563 "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
564 "\r\000/"; /* 0x60 - 0x6f */ 564 "\r\000/"; /* 0x60 - 0x6f */
565 565
566static bool sysrq_down; 566struct sysrq_state {
567static int sysrq_alt_use; 567 struct input_handle handle;
568static int sysrq_alt; 568 struct work_struct reinject_work;
569static DEFINE_SPINLOCK(sysrq_event_lock); 569 unsigned long key_down[BITS_TO_LONGS(KEY_CNT)];
570 unsigned int alt;
571 unsigned int alt_use;
572 bool active;
573 bool need_reinject;
574};
575
576static void sysrq_reinject_alt_sysrq(struct work_struct *work)
577{
578 struct sysrq_state *sysrq =
579 container_of(work, struct sysrq_state, reinject_work);
580 struct input_handle *handle = &sysrq->handle;
581 unsigned int alt_code = sysrq->alt_use;
582
583 if (sysrq->need_reinject) {
584 /* Simulate press and release of Alt + SysRq */
585 input_inject_event(handle, EV_KEY, alt_code, 1);
586 input_inject_event(handle, EV_KEY, KEY_SYSRQ, 1);
587 input_inject_event(handle, EV_SYN, SYN_REPORT, 1);
588
589 input_inject_event(handle, EV_KEY, KEY_SYSRQ, 0);
590 input_inject_event(handle, EV_KEY, alt_code, 0);
591 input_inject_event(handle, EV_SYN, SYN_REPORT, 1);
592 }
593}
570 594
571static bool sysrq_filter(struct input_handle *handle, unsigned int type, 595static bool sysrq_filter(struct input_handle *handle,
572 unsigned int code, int value) 596 unsigned int type, unsigned int code, int value)
573{ 597{
598 struct sysrq_state *sysrq = handle->private;
599 bool was_active = sysrq->active;
574 bool suppress; 600 bool suppress;
575 601
576 /* We are called with interrupts disabled, just take the lock */ 602 switch (type) {
577 spin_lock(&sysrq_event_lock);
578 603
579 if (type != EV_KEY) 604 case EV_SYN:
580 goto out; 605 suppress = false;
606 break;
581 607
582 switch (code) { 608 case EV_KEY:
609 switch (code) {
583 610
584 case KEY_LEFTALT: 611 case KEY_LEFTALT:
585 case KEY_RIGHTALT: 612 case KEY_RIGHTALT:
586 if (value) 613 if (!value) {
587 sysrq_alt = code; 614 /* One of ALTs is being released */
588 else { 615 if (sysrq->active && code == sysrq->alt_use)
589 if (sysrq_down && code == sysrq_alt_use) 616 sysrq->active = false;
590 sysrq_down = false;
591 617
592 sysrq_alt = 0; 618 sysrq->alt = KEY_RESERVED;
619
620 } else if (value != 2) {
621 sysrq->alt = code;
622 sysrq->need_reinject = false;
623 }
624 break;
625
626 case KEY_SYSRQ:
627 if (value == 1 && sysrq->alt != KEY_RESERVED) {
628 sysrq->active = true;
629 sysrq->alt_use = sysrq->alt;
630 /*
631 * If nothing else will be pressed we'll need
632 * to * re-inject Alt-SysRq keysroke.
633 */
634 sysrq->need_reinject = true;
635 }
636
637 /*
638 * Pretend that sysrq was never pressed at all. This
639 * is needed to properly handle KGDB which will try
640 * to release all keys after exiting debugger. If we
641 * do not clear key bit it KGDB will end up sending
642 * release events for Alt and SysRq, potentially
643 * triggering print screen function.
644 */
645 if (sysrq->active)
646 clear_bit(KEY_SYSRQ, handle->dev->key);
647
648 break;
649
650 default:
651 if (sysrq->active && value && value != 2) {
652 sysrq->need_reinject = false;
653 __handle_sysrq(sysrq_xlate[code], true);
654 }
655 break;
593 } 656 }
594 break;
595 657
596 case KEY_SYSRQ: 658 suppress = sysrq->active;
597 if (value == 1 && sysrq_alt) { 659
598 sysrq_down = true; 660 if (!sysrq->active) {
599 sysrq_alt_use = sysrq_alt; 661 /*
662 * If we are not suppressing key presses keep track of
663 * keyboard state so we can release keys that have been
664 * pressed before entering SysRq mode.
665 */
666 if (value)
667 set_bit(code, sysrq->key_down);
668 else
669 clear_bit(code, sysrq->key_down);
670
671 if (was_active)
672 schedule_work(&sysrq->reinject_work);
673
674 } else if (value == 0 &&
675 test_and_clear_bit(code, sysrq->key_down)) {
676 /*
677 * Pass on release events for keys that was pressed before
678 * entering SysRq mode.
679 */
680 suppress = false;
600 } 681 }
601 break; 682 break;
602 683
603 default: 684 default:
604 if (sysrq_down && value && value != 2) 685 suppress = sysrq->active;
605 __handle_sysrq(sysrq_xlate[code], true);
606 break; 686 break;
607 } 687 }
608 688
609out:
610 suppress = sysrq_down;
611 spin_unlock(&sysrq_event_lock);
612
613 return suppress; 689 return suppress;
614} 690}
615 691
@@ -617,28 +693,28 @@ static int sysrq_connect(struct input_handler *handler,
617 struct input_dev *dev, 693 struct input_dev *dev,
618 const struct input_device_id *id) 694 const struct input_device_id *id)
619{ 695{
620 struct input_handle *handle; 696 struct sysrq_state *sysrq;
621 int error; 697 int error;
622 698
623 sysrq_down = false; 699 sysrq = kzalloc(sizeof(struct sysrq_state), GFP_KERNEL);
624 sysrq_alt = 0; 700 if (!sysrq)
625
626 handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
627 if (!handle)
628 return -ENOMEM; 701 return -ENOMEM;
629 702
630 handle->dev = dev; 703 INIT_WORK(&sysrq->reinject_work, sysrq_reinject_alt_sysrq);
631 handle->handler = handler; 704
632 handle->name = "sysrq"; 705 sysrq->handle.dev = dev;
706 sysrq->handle.handler = handler;
707 sysrq->handle.name = "sysrq";
708 sysrq->handle.private = sysrq;
633 709
634 error = input_register_handle(handle); 710 error = input_register_handle(&sysrq->handle);
635 if (error) { 711 if (error) {
636 pr_err("Failed to register input sysrq handler, error %d\n", 712 pr_err("Failed to register input sysrq handler, error %d\n",
637 error); 713 error);
638 goto err_free; 714 goto err_free;
639 } 715 }
640 716
641 error = input_open_device(handle); 717 error = input_open_device(&sysrq->handle);
642 if (error) { 718 if (error) {
643 pr_err("Failed to open input device, error %d\n", error); 719 pr_err("Failed to open input device, error %d\n", error);
644 goto err_unregister; 720 goto err_unregister;
@@ -647,17 +723,20 @@ static int sysrq_connect(struct input_handler *handler,
647 return 0; 723 return 0;
648 724
649 err_unregister: 725 err_unregister:
650 input_unregister_handle(handle); 726 input_unregister_handle(&sysrq->handle);
651 err_free: 727 err_free:
652 kfree(handle); 728 kfree(sysrq);
653 return error; 729 return error;
654} 730}
655 731
656static void sysrq_disconnect(struct input_handle *handle) 732static void sysrq_disconnect(struct input_handle *handle)
657{ 733{
734 struct sysrq_state *sysrq = handle->private;
735
658 input_close_device(handle); 736 input_close_device(handle);
737 cancel_work_sync(&sysrq->reinject_work);
659 input_unregister_handle(handle); 738 input_unregister_handle(handle);
660 kfree(handle); 739 kfree(sysrq);
661} 740}
662 741
663/* 742/*