diff options
Diffstat (limited to 'drivers/tty/hvc/hvc_iucv.c')
-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 9d47f50c275..fd17a9b804b 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) } /** + * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS + * @hp: Pointer the HVC device (struct hvc_struct) + * @raise: Non-zero to raise or zero to lower DTR/RTS lines + * + * This routine notifies the HVC back-end to raise or lower DTR/RTS + * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to + * drop the IUCV connection (similar to hang up the modem). + */ +static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) +{ + struct hvc_iucv_private *priv; + struct iucv_path *path; + + /* Raising the DTR/RTS is ignored as IUCV connections can be + * established at any times. + */ + if (raise) + return; + + priv = hvc_iucv_get_private(hp->vtermno); + if (!priv) + return; + + /* Lowering the DTR/RTS lines disconnects an established IUCV + * connection. + */ + flush_sndbuf_sync(priv); + + spin_lock_bh(&priv->lock); + path = priv->path; /* save reference to IUCV path */ + priv->path = NULL; + priv->iucv_state = IUCV_DISCONN; + spin_unlock_bh(&priv->lock); + + /* Sever IUCV path outside of priv->lock due to lock ordering of: + * priv->lock <--> iucv_table_lock */ + if (path) { + iucv_path_sever(path, NULL); + iucv_path_free(path); + } +} + +/** * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. * @hp: Pointer to the HVC device (struct hvc_struct) * @id: Additional data (originally passed to hvc_alloc): * the index of an struct hvc_iucv_private instance. * * This routine notifies the HVC back-end that the last tty device fd has been - * closed. The function calls hvc_iucv_cleanup() to clean up the struct - * hvc_iucv_private instance. + * closed. The function cleans up tty resources. The clean-up of the IUCV + * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios + * control setting. * * Locking: struct hvc_iucv_private->lock */ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) { struct hvc_iucv_private *priv; - struct iucv_path *path; priv = hvc_iucv_get_private(id); if (!priv) @@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) flush_sndbuf_sync(priv); spin_lock_bh(&priv->lock); - path = priv->path; /* save reference to IUCV path */ - priv->path = NULL; - hvc_iucv_cleanup(priv); + destroy_tty_buffer_list(&priv->tty_outqueue); + destroy_tty_buffer_list(&priv->tty_inqueue); + priv->tty_state = TTY_CLOSED; + priv->sndbuf_len = 0; spin_unlock_bh(&priv->lock); - - /* sever IUCV path outside of priv->lock due to lock ordering of: - * priv->lock <--> iucv_table_lock */ - if (path) { - iucv_path_sever(path, NULL); - iucv_path_free(path); - } } /** @@ -931,6 +968,7 @@ static const struct hv_ops hvc_iucv_ops = { .notifier_add = hvc_iucv_notifier_add, .notifier_del = hvc_iucv_notifier_del, .notifier_hangup = hvc_iucv_notifier_hangup, + .dtr_rts = hvc_iucv_dtr_rts, }; /* Suspend / resume device operations */ |