diff options
Diffstat (limited to 'drivers/tty/tty_port.c')
| -rw-r--r-- | drivers/tty/tty_port.c | 109 | 
1 files changed, 79 insertions, 30 deletions
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index b7ff59d3db8..121aeb9393e 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -132,6 +132,7 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf);   */  void tty_port_destroy(struct tty_port *port)  { +	cancel_work_sync(&port->buf.work);  	tty_buffer_free_all(port);  }  EXPORT_SYMBOL(tty_port_destroy); @@ -196,12 +197,24 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)  }  EXPORT_SYMBOL(tty_port_tty_set); -static void tty_port_shutdown(struct tty_port *port) +static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)  {  	mutex_lock(&port->mutex); -	if (port->ops->shutdown && !port->console && -		test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) +	if (port->console) +		goto out; + +	if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { +		/* +		 * Drop DTR/RTS if HUPCL is set. This causes any attached +		 * modem to hang up the line. +		 */ +		if (tty && C_HUPCL(tty)) +			tty_port_lower_dtr_rts(port); + +		if (port->ops->shutdown)  			port->ops->shutdown(port); +	} +out:  	mutex_unlock(&port->mutex);  } @@ -215,24 +228,58 @@ static void tty_port_shutdown(struct tty_port *port)  void tty_port_hangup(struct tty_port *port)  { +	struct tty_struct *tty;  	unsigned long flags;  	spin_lock_irqsave(&port->lock, flags);  	port->count = 0;  	port->flags &= ~ASYNC_NORMAL_ACTIVE; -	if (port->tty) { -		set_bit(TTY_IO_ERROR, &port->tty->flags); -		tty_kref_put(port->tty); -	} +	tty = port->tty; +	if (tty) +		set_bit(TTY_IO_ERROR, &tty->flags);  	port->tty = NULL;  	spin_unlock_irqrestore(&port->lock, flags); +	tty_port_shutdown(port, tty); +	tty_kref_put(tty);  	wake_up_interruptible(&port->open_wait);  	wake_up_interruptible(&port->delta_msr_wait); -	tty_port_shutdown(port);  }  EXPORT_SYMBOL(tty_port_hangup);  /** + * tty_port_tty_hangup - helper to hang up a tty + * + * @port: tty port + * @check_clocal: hang only ttys with CLOCAL unset? + */ +void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) +{ +	struct tty_struct *tty = tty_port_tty_get(port); + +	if (tty && (!check_clocal || !C_CLOCAL(tty))) { +		tty_hangup(tty); +		tty_kref_put(tty); +	} +} +EXPORT_SYMBOL_GPL(tty_port_tty_hangup); + +/** + * tty_port_tty_wakeup - helper to wake up a tty + * + * @port: tty port + */ +void tty_port_tty_wakeup(struct tty_port *port) +{ +	struct tty_struct *tty = tty_port_tty_get(port); + +	if (tty) { +		tty_wakeup(tty); +		tty_kref_put(tty); +	} +} +EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); + +/**   *	tty_port_carrier_raised	-	carrier raised check   *	@port: tty port   * @@ -350,7 +397,7 @@ int tty_port_block_til_ready(struct tty_port *port,  	while (1) {  		/* Indicate we are open */ -		if (tty->termios.c_cflag & CBAUD) +		if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags))  			tty_port_raise_dtr_rts(port);  		prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); @@ -395,6 +442,20 @@ int tty_port_block_til_ready(struct tty_port *port,  }  EXPORT_SYMBOL(tty_port_block_til_ready); +static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) +{ +	unsigned int bps = tty_get_baud_rate(tty); +	long timeout; + +	if (bps > 1200) { +		timeout = (HZ * 10 * port->drain_delay) / bps; +		timeout = max_t(long, timeout, HZ / 10); +	} else { +		timeout = 2 * HZ; +	} +	schedule_timeout_interruptible(timeout); +} +  int tty_port_close_start(struct tty_port *port,  				struct tty_struct *tty, struct file *filp)  { @@ -427,31 +488,19 @@ int tty_port_close_start(struct tty_port *port,  	set_bit(ASYNCB_CLOSING, &port->flags);  	tty->closing = 1;  	spin_unlock_irqrestore(&port->lock, flags); -	/* Don't block on a stalled port, just pull the chain */ -	if (tty->flow_stopped) -		tty_driver_flush_buffer(tty); -	if (test_bit(ASYNCB_INITIALIZED, &port->flags) && -			port->closing_wait != ASYNC_CLOSING_WAIT_NONE) -		tty_wait_until_sent_from_close(tty, port->closing_wait); -	if (port->drain_delay) { -		unsigned int bps = tty_get_baud_rate(tty); -		long timeout; -		if (bps > 1200) -			timeout = max_t(long, -				(HZ * 10 * port->drain_delay) / bps, HZ / 10); -		else -			timeout = 2 * HZ; -		schedule_timeout_interruptible(timeout); +	if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { +		/* Don't block on a stalled port, just pull the chain */ +		if (tty->flow_stopped) +			tty_driver_flush_buffer(tty); +		if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) +			tty_wait_until_sent_from_close(tty, port->closing_wait); +		if (port->drain_delay) +			tty_port_drain_delay(port, tty);  	}  	/* Flush the ldisc buffering */  	tty_ldisc_flush(tty); -	/* Drop DTR/RTS if HUPCL is set. This causes any attached modem to -	   hang up the line */ -	if (tty->termios.c_cflag & HUPCL) -		tty_port_lower_dtr_rts(port); -  	/* Don't call port->drop for the last reference. Callers will want  	   to drop the last active reference in ->shutdown() or the tty  	   shutdown path */ @@ -486,7 +535,7 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty,  {  	if (tty_port_close_start(port, tty, filp) == 0)  		return; -	tty_port_shutdown(port); +	tty_port_shutdown(port, tty);  	set_bit(TTY_IO_ERROR, &tty->flags);  	tty_port_close_end(port, tty);  	tty_port_tty_set(port, NULL);  |