diff options
-rw-r--r-- | drivers/tty/hvc/hvc_iucv.c | 64 |
1 files changed, 51 insertions, 13 deletions
diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index 9d47f50c2755..fd17a9b804b8 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c | |||
@@ -656,21 +656,64 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) | |||
656 | } | 656 | } |
657 | 657 | ||
658 | /** | 658 | /** |
659 | * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS | ||
660 | * @hp: Pointer the HVC device (struct hvc_struct) | ||
661 | * @raise: Non-zero to raise or zero to lower DTR/RTS lines | ||
662 | * | ||
663 | * This routine notifies the HVC back-end to raise or lower DTR/RTS | ||
664 | * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to | ||
665 | * drop the IUCV connection (similar to hang up the modem). | ||
666 | */ | ||
667 | static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) | ||
668 | { | ||
669 | struct hvc_iucv_private *priv; | ||
670 | struct iucv_path *path; | ||
671 | |||
672 | /* Raising the DTR/RTS is ignored as IUCV connections can be | ||
673 | * established at any times. | ||
674 | */ | ||
675 | if (raise) | ||
676 | return; | ||
677 | |||
678 | priv = hvc_iucv_get_private(hp->vtermno); | ||
679 | if (!priv) | ||
680 | return; | ||
681 | |||
682 | /* Lowering the DTR/RTS lines disconnects an established IUCV | ||
683 | * connection. | ||
684 | */ | ||
685 | flush_sndbuf_sync(priv); | ||
686 | |||
687 | spin_lock_bh(&priv->lock); | ||
688 | path = priv->path; /* save reference to IUCV path */ | ||
689 | priv->path = NULL; | ||
690 | priv->iucv_state = IUCV_DISCONN; | ||
691 | spin_unlock_bh(&priv->lock); | ||
692 | |||
693 | /* Sever IUCV path outside of priv->lock due to lock ordering of: | ||
694 | * priv->lock <--> iucv_table_lock */ | ||
695 | if (path) { | ||
696 | iucv_path_sever(path, NULL); | ||
697 | iucv_path_free(path); | ||
698 | } | ||
699 | } | ||
700 | |||
701 | /** | ||
659 | * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. | 702 | * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. |
660 | * @hp: Pointer to the HVC device (struct hvc_struct) | 703 | * @hp: Pointer to the HVC device (struct hvc_struct) |
661 | * @id: Additional data (originally passed to hvc_alloc): | 704 | * @id: Additional data (originally passed to hvc_alloc): |
662 | * the index of an struct hvc_iucv_private instance. | 705 | * the index of an struct hvc_iucv_private instance. |
663 | * | 706 | * |
664 | * This routine notifies the HVC back-end that the last tty device fd has been | 707 | * This routine notifies the HVC back-end that the last tty device fd has been |
665 | * closed. The function calls hvc_iucv_cleanup() to clean up the struct | 708 | * closed. The function cleans up tty resources. The clean-up of the IUCV |
666 | * hvc_iucv_private instance. | 709 | * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios |
710 | * control setting. | ||
667 | * | 711 | * |
668 | * Locking: struct hvc_iucv_private->lock | 712 | * Locking: struct hvc_iucv_private->lock |
669 | */ | 713 | */ |
670 | static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) | 714 | static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) |
671 | { | 715 | { |
672 | struct hvc_iucv_private *priv; | 716 | struct hvc_iucv_private *priv; |
673 | struct iucv_path *path; | ||
674 | 717 | ||
675 | priv = hvc_iucv_get_private(id); | 718 | priv = hvc_iucv_get_private(id); |
676 | if (!priv) | 719 | if (!priv) |
@@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) | |||
679 | flush_sndbuf_sync(priv); | 722 | flush_sndbuf_sync(priv); |
680 | 723 | ||
681 | spin_lock_bh(&priv->lock); | 724 | spin_lock_bh(&priv->lock); |
682 | path = priv->path; /* save reference to IUCV path */ | 725 | destroy_tty_buffer_list(&priv->tty_outqueue); |
683 | priv->path = NULL; | 726 | destroy_tty_buffer_list(&priv->tty_inqueue); |
684 | hvc_iucv_cleanup(priv); | 727 | priv->tty_state = TTY_CLOSED; |
728 | priv->sndbuf_len = 0; | ||
685 | spin_unlock_bh(&priv->lock); | 729 | spin_unlock_bh(&priv->lock); |
686 | |||
687 | /* sever IUCV path outside of priv->lock due to lock ordering of: | ||
688 | * priv->lock <--> iucv_table_lock */ | ||
689 | if (path) { | ||
690 | iucv_path_sever(path, NULL); | ||
691 | iucv_path_free(path); | ||
692 | } | ||
693 | } | 730 | } |
694 | 731 | ||
695 | /** | 732 | /** |
@@ -931,6 +968,7 @@ static const struct hv_ops hvc_iucv_ops = { | |||
931 | .notifier_add = hvc_iucv_notifier_add, | 968 | .notifier_add = hvc_iucv_notifier_add, |
932 | .notifier_del = hvc_iucv_notifier_del, | 969 | .notifier_del = hvc_iucv_notifier_del, |
933 | .notifier_hangup = hvc_iucv_notifier_hangup, | 970 | .notifier_hangup = hvc_iucv_notifier_hangup, |
971 | .dtr_rts = hvc_iucv_dtr_rts, | ||
934 | }; | 972 | }; |
935 | 973 | ||
936 | /* Suspend / resume device operations */ | 974 | /* Suspend / resume device operations */ |