diff options
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/n_gsm.c | 6 | ||||
-rw-r--r-- | drivers/tty/sysrq.c | 169 | ||||
-rw-r--r-- | drivers/tty/tty_io.c | 13 | ||||
-rw-r--r-- | drivers/tty/tty_ldisc.c | 2 |
4 files changed, 141 insertions, 49 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 81b46585edf7..c5f8e5bda2b2 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c | |||
@@ -716,8 +716,8 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) | |||
716 | if (msg->len < 128) | 716 | if (msg->len < 128) |
717 | *--dp = (msg->len << 1) | EA; | 717 | *--dp = (msg->len << 1) | EA; |
718 | else { | 718 | else { |
719 | *--dp = ((msg->len & 127) << 1) | EA; | 719 | *--dp = (msg->len >> 7); /* bits 7 - 15 */ |
720 | *--dp = (msg->len >> 6) & 0xfe; | 720 | *--dp = (msg->len & 127) << 1; /* bits 0 - 6 */ |
721 | } | 721 | } |
722 | } | 722 | } |
723 | 723 | ||
@@ -968,6 +968,8 @@ static void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data, | |||
968 | { | 968 | { |
969 | struct gsm_msg *msg; | 969 | struct gsm_msg *msg; |
970 | msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype); | 970 | msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype); |
971 | if (msg == NULL) | ||
972 | return; | ||
971 | msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */ | 973 | msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */ |
972 | msg->data[1] = (dlen << 1) | EA; | 974 | msg->data[1] = (dlen << 1) | EA; |
973 | memcpy(msg->data + 2, data, dlen); | 975 | memcpy(msg->data + 2, data, dlen); |
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 | /* |
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index c05c5af5aa04..35480dd57a30 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c | |||
@@ -559,6 +559,9 @@ void __tty_hangup(struct tty_struct *tty) | |||
559 | 559 | ||
560 | tty_lock(); | 560 | tty_lock(); |
561 | 561 | ||
562 | /* some functions below drop BTM, so we need this bit */ | ||
563 | set_bit(TTY_HUPPING, &tty->flags); | ||
564 | |||
562 | /* inuse_filps is protected by the single tty lock, | 565 | /* inuse_filps is protected by the single tty lock, |
563 | this really needs to change if we want to flush the | 566 | this really needs to change if we want to flush the |
564 | workqueue with the lock held */ | 567 | workqueue with the lock held */ |
@@ -578,6 +581,10 @@ void __tty_hangup(struct tty_struct *tty) | |||
578 | } | 581 | } |
579 | spin_unlock(&tty_files_lock); | 582 | spin_unlock(&tty_files_lock); |
580 | 583 | ||
584 | /* | ||
585 | * it drops BTM and thus races with reopen | ||
586 | * we protect the race by TTY_HUPPING | ||
587 | */ | ||
581 | tty_ldisc_hangup(tty); | 588 | tty_ldisc_hangup(tty); |
582 | 589 | ||
583 | read_lock(&tasklist_lock); | 590 | read_lock(&tasklist_lock); |
@@ -615,7 +622,6 @@ void __tty_hangup(struct tty_struct *tty) | |||
615 | tty->session = NULL; | 622 | tty->session = NULL; |
616 | tty->pgrp = NULL; | 623 | tty->pgrp = NULL; |
617 | tty->ctrl_status = 0; | 624 | tty->ctrl_status = 0; |
618 | set_bit(TTY_HUPPED, &tty->flags); | ||
619 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | 625 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); |
620 | 626 | ||
621 | /* Account for the p->signal references we killed */ | 627 | /* Account for the p->signal references we killed */ |
@@ -641,6 +647,7 @@ void __tty_hangup(struct tty_struct *tty) | |||
641 | * can't yet guarantee all that. | 647 | * can't yet guarantee all that. |
642 | */ | 648 | */ |
643 | set_bit(TTY_HUPPED, &tty->flags); | 649 | set_bit(TTY_HUPPED, &tty->flags); |
650 | clear_bit(TTY_HUPPING, &tty->flags); | ||
644 | tty_ldisc_enable(tty); | 651 | tty_ldisc_enable(tty); |
645 | 652 | ||
646 | tty_unlock(); | 653 | tty_unlock(); |
@@ -1310,7 +1317,9 @@ static int tty_reopen(struct tty_struct *tty) | |||
1310 | { | 1317 | { |
1311 | struct tty_driver *driver = tty->driver; | 1318 | struct tty_driver *driver = tty->driver; |
1312 | 1319 | ||
1313 | if (test_bit(TTY_CLOSING, &tty->flags)) | 1320 | if (test_bit(TTY_CLOSING, &tty->flags) || |
1321 | test_bit(TTY_HUPPING, &tty->flags) || | ||
1322 | test_bit(TTY_LDISC_CHANGING, &tty->flags)) | ||
1314 | return -EIO; | 1323 | return -EIO; |
1315 | 1324 | ||
1316 | if (driver->type == TTY_DRIVER_TYPE_PTY && | 1325 | if (driver->type == TTY_DRIVER_TYPE_PTY && |
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index d8e96b005023..4214d58276f7 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c | |||
@@ -454,6 +454,8 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) | |||
454 | /* BTM here locks versus a hangup event */ | 454 | /* BTM here locks versus a hangup event */ |
455 | WARN_ON(!tty_locked()); | 455 | WARN_ON(!tty_locked()); |
456 | ret = ld->ops->open(tty); | 456 | ret = ld->ops->open(tty); |
457 | if (ret) | ||
458 | clear_bit(TTY_LDISC_OPEN, &tty->flags); | ||
457 | return ret; | 459 | return ret; |
458 | } | 460 | } |
459 | return 0; | 461 | return 0; |