diff options
Diffstat (limited to 'drivers/tty/sysrq.c')
| -rw-r--r-- | drivers/tty/sysrq.c | 169 |
1 files changed, 124 insertions, 45 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index eaa5d3efa79d..c556ed9db13d 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/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 */ |
| 557 | static const unsigned char sysrq_xlate[KEY_MAX + 1] = | 557 | static 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 | ||
| 566 | static bool sysrq_down; | 566 | struct sysrq_state { |
| 567 | static int sysrq_alt_use; | 567 | struct input_handle handle; |
| 568 | static int sysrq_alt; | 568 | struct work_struct reinject_work; |
| 569 | static 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 | |||
| 576 | static 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 | ||
| 571 | static bool sysrq_filter(struct input_handle *handle, unsigned int type, | 595 | static 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 | ||
| 609 | out: | ||
| 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 | ||
| 656 | static void sysrq_disconnect(struct input_handle *handle) | 732 | static 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 | /* |
