Commit a6614999 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

tty: Introduce some close helpers for ports

Again this is a lot of common code we can unify
Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 7834909f
......@@ -945,76 +945,30 @@ static void isicom_flush_buffer(struct tty_struct *tty)
static void isicom_close(struct tty_struct *tty, struct file *filp)
{
struct isi_port *port = tty->driver_data;
struct isi_port *ip = tty->driver_data;
struct tty_port *port = &ip->port;
struct isi_board *card;
unsigned long flags;
if (!port)
return;
card = port->card;
if (isicom_paranoia_check(port, tty->name, "isicom_close"))
return;
pr_dbg("Close start!!!.\n");
spin_lock_irqsave(&port->port.lock, flags);
if (tty_hung_up_p(filp)) {
spin_unlock_irqrestore(&port->port.lock, flags);
return;
}
if (tty->count == 1 && port->port.count != 1) {
printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port "
"count tty->count = 1 port count = %d.\n",
card->base, port->port.count);
port->port.count = 1;
}
if (--port->port.count < 0) {
printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port "
"count for channel%d = %d", card->base, port->channel,
port->port.count);
port->port.count = 0;
}
BUG_ON(!ip);
if (port->port.count) {
spin_unlock_irqrestore(&port->port.lock, flags);
card = ip->card;
if (isicom_paranoia_check(ip, tty->name, "isicom_close"))
return;
}
port->port.flags |= ASYNC_CLOSING;
tty->closing = 1;
spin_unlock_irqrestore(&port->port.lock, flags);
if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, port->port.closing_wait);
/* indicate to the card that no more data can be received
on this port */
spin_lock_irqsave(&card->card_lock, flags);
if (port->port.flags & ASYNC_INITIALIZED) {
card->port_status &= ~(1 << port->channel);
if (port->flags & ASYNC_INITIALIZED) {
card->port_status &= ~(1 << ip->channel);
outw(card->port_status, card->base + 0x02);
}
isicom_shutdown_port(port);
isicom_shutdown_port(ip);
spin_unlock_irqrestore(&card->card_lock, flags);
isicom_flush_buffer(tty);
tty_ldisc_flush(tty);
spin_lock_irqsave(&port->port.lock, flags);
tty->closing = 0;
if (port->port.blocked_open) {
spin_unlock_irqrestore(&port->port.lock, flags);
if (port->port.close_delay) {
pr_dbg("scheduling until time out.\n");
msleep_interruptible(
jiffies_to_msecs(port->port.close_delay));
}
spin_lock_irqsave(&port->port.lock, flags);
wake_up_interruptible(&port->port.open_wait);
}
port->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
wake_up_interruptible(&port->port.close_wait);
spin_unlock_irqrestore(&port->port.lock, flags);
tty_port_close_end(port, tty);
}
/* write et all */
......
This diff is collapsed.
......@@ -1080,57 +1080,26 @@ static void mxser_flush_buffer(struct tty_struct *tty)
static void mxser_close(struct tty_struct *tty, struct file *filp)
{
struct mxser_port *info = tty->driver_data;
struct tty_port *port = &info->port;
unsigned long timeout;
unsigned long flags;
if (tty->index == MXSER_PORTS)
return;
if (!info)
return;
spin_lock_irqsave(&info->port.lock, flags);
if (tty_hung_up_p(filp)) {
spin_unlock_irqrestore(&info->port.lock, flags);
return;
}
if ((tty->count == 1) && (info->port.count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. Info->port.count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk(KERN_ERR "mxser_close: bad serial port count; "
"tty->count is 1, info->port.count is %d\n", info->port.count);
info->port.count = 1;
}
if (--info->port.count < 0) {
printk(KERN_ERR "mxser_close: bad serial port count for "
"ttys%d: %d\n", tty->index, info->port.count);
info->port.count = 0;
}
if (info->port.count) {
spin_unlock_irqrestore(&info->port.lock, flags);
if (tty_port_close_start(port, tty, filp) == 0)
return;
}
info->port.flags |= ASYNC_CLOSING;
spin_unlock_irqrestore(&info->port.lock, flags);
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*
* FIXME: Can this go ?
*/
if (info->port.flags & ASYNC_NORMAL_ACTIVE)
info->normal_termios = *tty->termios;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, info->port.closing_wait);
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
......@@ -1156,19 +1125,12 @@ static void mxser_close(struct tty_struct *tty, struct file *filp)
}
}
mxser_shutdown(tty);
mxser_flush_buffer(tty);
tty_ldisc_flush(tty);
tty->closing = 0;
tty_port_tty_set(&info->port, NULL);
if (info->port.blocked_open) {
if (info->port.close_delay)
schedule_timeout_interruptible(info->port.close_delay);
wake_up_interruptible(&info->port.open_wait);
}
info->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
/* Right now the tty_port set is done outside of the close_end helper
as we don't yet have everyone using refcounts */
tty_port_close_end(port, tty);
tty_port_tty_set(port, NULL);
}
static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count)
......
......@@ -929,35 +929,11 @@ static void rc_close(struct tty_struct *tty, struct file *filp)
if (!port || rc_paranoia_check(port, tty->name, "close"))
return;
spin_lock_irqsave(&port->port.lock, flags);
if (tty_hung_up_p(filp))
goto out;
bp = port_Board(port);
if ((tty->count == 1) && (port->port.count != 1)) {
printk(KERN_INFO "rc%d: rc_close: bad port count;"
" tty->count is 1, port count is %d\n",
board_No(bp), port->port.count);
port->port.count = 1;
}
if (--port->port.count < 0) {
printk(KERN_INFO "rc%d: rc_close: bad port count "
"for tty%d: %d\n",
board_No(bp), port_No(port), port->port.count);
port->port.count = 0;
}
if (port->port.count)
goto out;
port->port.flags |= ASYNC_CLOSING;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
spin_unlock_irqrestore(&port->port.lock, flags);
if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, port->port.closing_wait);
if (tty_port_close_start(&port->port, tty, filp) == 0)
return;
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
......@@ -989,23 +965,8 @@ static void rc_close(struct tty_struct *tty, struct file *filp)
rc_shutdown_port(tty, bp, port);
rc_flush_buffer(tty);
spin_unlock_irqrestore(&riscom_lock, flags);
tty_ldisc_flush(tty);
spin_lock_irqsave(&port->port.lock, flags);
tty->closing = 0;
port->port.tty = NULL;
if (port->port.blocked_open) {
spin_unlock_irqrestore(&port->port.lock, flags);
if (port->port.close_delay)
msleep_interruptible(jiffies_to_msecs(port->port.close_delay));
wake_up_interruptible(&port->port.open_wait);
spin_lock_irqsave(&port->port.lock, flags);
}
port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&port->port.close_wait);
out:
spin_unlock_irqrestore(&riscom_lock, flags);
tty_port_close_end(&port->port, tty);
}
static int rc_write(struct tty_struct *tty,
......
......@@ -833,40 +833,20 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp);
portp = tty->driver_data;
if (portp == NULL)
return;
BUG_ON(portp == NULL);
port = &portp->port;
spin_lock_irqsave(&port->lock, flags);
if (tty_hung_up_p(filp)) {
spin_unlock_irqrestore(&port->lock, flags);
if (tty_port_close_start(port, tty, filp) == 0)
return;
}
if (tty->count == 1 && port->count != 1)
port->count = 1;
if (port->count-- > 1) {
spin_unlock_irqrestore(&port->lock, flags);
return;
}
port->count = 0;
port->flags |= ASYNC_CLOSING;
/*
* May want to wait for any data to drain before closing. The BUSY
* flag keeps track of whether we are still sending or not - it is
* very accurate for the cd1400, not quite so for the sc26198.
* (The sc26198 has no "end-of-data" interrupt only empty FIFO)
*/
tty->closing = 1;
spin_unlock_irqrestore(&port->lock, flags);
if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, portp->closing_wait);
stl_waituntilsent(tty, (HZ / 2));
spin_lock_irqsave(&port->lock, flags);
portp->port.flags &= ~ASYNC_INITIALIZED;
spin_unlock_irqrestore(&port->lock, flags);
......@@ -883,20 +863,9 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
portp->tx.head = NULL;
portp->tx.tail = NULL;
}
set_bit(TTY_IO_ERROR, &tty->flags);
tty_ldisc_flush(tty);
tty->closing = 0;
tty_port_close_end(port, tty);
tty_port_tty_set(port, NULL);
if (port->blocked_open) {
if (portp->close_delay)
msleep_interruptible(jiffies_to_msecs(portp->close_delay));
wake_up_interruptible(&portp->port.open_wait);
}
portp->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&port->close_wait);
}
/*****************************************************************************/
......
......@@ -3104,70 +3104,18 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgsl_close(%s) entry, count=%d\n",
__FILE__,__LINE__, info->device_name, info->port.count);
if (!info->port.count)
return;
if (tty_hung_up_p(filp))
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
if ((tty->count == 1) && (info->port.count != 1)) {
/*
* tty->count is 1 and the tty structure will be freed.
* info->port.count should be one in this case.
* if it's not, correct it so that the port is shutdown.
*/
printk("mgsl_close: bad refcount; tty->count is 1, "
"info->port.count is %d\n", info->port.count);
info->port.count = 1;
}
info->port.count--;
/* if at least one open remaining, leave hardware active */
if (info->port.count)
goto cleanup;
info->port.flags |= ASYNC_CLOSING;
/* set tty->closing to notify line discipline to
* only process XON/XOFF characters. Only the N_TTY
* discipline appears to use this (ppp does not).
*/
tty->closing = 1;
/* wait for transmit data to clear all layers */
if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) {
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgsl_close(%s) calling tty_wait_until_sent\n",
__FILE__,__LINE__, info->device_name );
tty_wait_until_sent(tty, info->port.closing_wait);
}
if (info->port.flags & ASYNC_INITIALIZED)
mgsl_wait_until_sent(tty, info->timeout);
mgsl_flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(info);
tty->closing = 0;
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
if (info->port.blocked_open) {
if (info->port.close_delay) {
msleep_interruptible(jiffies_to_msecs(info->port.close_delay));
}
wake_up_interruptible(&info->port.open_wait);
}
info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&info->port.close_wait);
cleanup:
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__,
......
......@@ -720,44 +720,9 @@ static void close(struct tty_struct *tty, struct file *filp)
return;
DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count));
if (!info->port.count)
return;
if (tty_hung_up_p(filp))
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
if ((tty->count == 1) && (info->port.count != 1)) {
/*
* tty->count is 1 and the tty structure will be freed.
* info->port.count should be one in this case.
* if it's not, correct it so that the port is shutdown.
*/
DBGERR(("%s close: bad refcount; tty->count=1, "
"info->port.count=%d\n", info->device_name, info->port.count));
info->port.count = 1;
}
info->port.count--;
/* if at least one open remaining, leave hardware active */
if (info->port.count)
goto cleanup;
info->port.flags |= ASYNC_CLOSING;
/* set tty->closing to notify line discipline to
* only process XON/XOFF characters. Only the N_TTY
* discipline appears to use this (ppp does not).
*/
tty->closing = 1;
/* wait for transmit data to clear all layers */
if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) {
DBGINFO(("%s call tty_wait_until_sent\n", info->device_name));
tty_wait_until_sent(tty, info->port.closing_wait);
}
if (info->port.flags & ASYNC_INITIALIZED)
wait_until_sent(tty, info->timeout);
flush_buffer(tty);
......@@ -765,20 +730,8 @@ static void close(struct tty_struct *tty, struct file *filp)
shutdown(info);
tty->closing = 0;
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
if (info->port.blocked_open) {
if (info->port.close_delay) {
msleep_interruptible(jiffies_to_msecs(info->port.close_delay));
}
wake_up_interruptible(&info->port.open_wait);
}
info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&info->port.close_wait);
cleanup:
DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count));
}
......
......@@ -810,70 +810,18 @@ static void close(struct tty_struct *tty, struct file *filp)
printk("%s(%d):%s close() entry, count=%d\n",
__FILE__,__LINE__, info->device_name, info->port.count);
if (!info->port.count)
return;
if (tty_hung_up_p(filp))
goto cleanup;
if ((tty->count == 1) && (info->port.count != 1)) {
/*
* tty->count is 1 and the tty structure will be freed.
* info->port.count should be one in this case.
* if it's not, correct it so that the port is shutdown.
*/
printk("%s(%d):%s close: bad refcount; tty->count is 1, "
"info->port.count is %d\n",
__FILE__,__LINE__, info->device_name, info->port.count);
info->port.count = 1;
}
info->port.count--;
/* if at least one open remaining, leave hardware active */
if (info->port.count)
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
info->port.flags |= ASYNC_CLOSING;
/* set tty->closing to notify line discipline to
* only process XON/XOFF characters. Only the N_TTY
* discipline appears to use this (ppp does not).
*/
tty->closing = 1;
/* wait for transmit data to clear all layers */
if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) {
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s close() calling tty_wait_until_sent\n",
__FILE__,__LINE__, info->device_name );
tty_wait_until_sent(tty, info->port.closing_wait);
}
if (info->port.flags & ASYNC_INITIALIZED)
wait_until_sent(tty, info->timeout);
flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(info);
tty->closing = 0;
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
if (info->port.blocked_open) {
if (info->port.close_delay) {
msleep_interruptible(jiffies_to_msecs(info->port.close_delay));
}
wake_up_interruptible(&info->port.open_wait);
}
info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&info->port.close_wait);
cleanup:
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__,
......
......@@ -257,3 +257,61 @@ int tty_port_block_til_ready(struct tty_port *port,
}
EXPORT_SYMBOL(tty_port_block_til_ready);
int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
if (tty_hung_up_p(filp)) {
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
if( tty->count == 1 && port->count != 1) {
printk(KERN_WARNING
"tty_port_close_start: tty->count = 1 port count = %d.\n",
port->count);
port->count = 1;
}
if (--port->count < 0) {
printk(KERN_WARNING "tty_port_close_start: count = %d\n",
port->count);
port->count = 0;
}
if (port->count) {
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
port->flags |= ASYNC_CLOSING;
tty->closing = 1;
spin_unlock_irqrestore(&port->lock, flags);
if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, port->closing_wait);
return 1;
}
EXPORT_SYMBOL(tty_port_close_start);
void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
{
unsigned long flags;
tty_ldisc_flush(tty);
spin_lock_irqsave(&port->lock, flags);
tty->closing = 0;
if (port->blocked_open) {
spin_unlock_irqrestore(&port->lock, flags);
if (port->close_delay) {
msleep_interruptible(
jiffies_to_msecs(port->close_delay));
}
spin_lock_irqsave(&port->lock, flags);
wake_up_interruptible(&port->open_wait);
}
port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
wake_up_interruptible(&port->close_wait);
spin_unlock_irqrestore(&port->lock, flags);
}
EXPORT_SYMBOL(tty_port_close_end);
......@@ -59,7 +59,6 @@ struct stliport {
unsigned int devnr;
int baud_base;
int custom_divisor;
int close_delay;
int closing_wait;
int rc;
int argsize;
......
......@@ -441,6 +441,9 @@ extern void tty_port_raise_dtr_rts(struct tty_port *port);
extern void tty_port_hangup(struct tty_port *port);
extern int tty_port_block_til_ready(struct tty_port *port,
struct tty_struct *tty, struct file *filp);
extern int tty_port_close_start(struct tty_port *port,
struct tty_struct *tty, struct file *filp);
extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty);
extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
extern int tty_unregister_ldisc(int disc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment