Skip to content

Commit

Permalink
tty: Introduce a tty_port generic block_til_ready
Browse files Browse the repository at this point in the history
Start sucking more commonality out of the drivers into a single piece of
core code.

Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Alan Cox authored and Linus Torvalds committed Jan 2, 2009
1 parent 3b6826b commit 36c621d
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 235 deletions.
79 changes: 2 additions & 77 deletions drivers/char/isicom.c
Original file line number Diff line number Diff line change
Expand Up @@ -838,82 +838,6 @@ static int isicom_carrier_raised(struct tty_port *port)
return (ip->status & ISI_DCD)?1 : 0;
}

static int block_til_ready(struct tty_struct *tty, struct file *filp,
struct isi_port *ip)
{
struct tty_port *port = &ip->port;
int do_clocal = 0, retval;
unsigned long flags;
DECLARE_WAITQUEUE(wait, current);
int cd;

/* block if port is in the process of being closed */

if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
pr_dbg("block_til_ready: close in progress.\n");
interruptible_sleep_on(&port->close_wait);
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
}

/* if non-blocking mode is set ... */

if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
pr_dbg("block_til_ready: non-block mode.\n");
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

if (C_CLOCAL(tty))
do_clocal = 1;

/* block waiting for DCD to be asserted, and while
callout dev is busy */
retval = 0;
add_wait_queue(&port->open_wait, &wait);

spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->count--;
port->blocked_open++;
spin_unlock_irqrestore(&port->lock, flags);

while (1) {
tty_port_raise_dtr_rts(port);

set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
break;
}
cd = tty_port_carrier_raised(port);
if (!(port->flags & ASYNC_CLOSING) &&
(do_clocal || cd))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->open_wait, &wait);
spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->count++;
port->blocked_open--;
if (retval == 0)
port->flags |= ASYNC_NORMAL_ACTIVE;
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}

static int isicom_open(struct tty_struct *tty, struct file *filp)
{
struct isi_port *port;
Expand All @@ -940,12 +864,13 @@ static int isicom_open(struct tty_struct *tty, struct file *filp)

isicom_setup_board(card);

/* FIXME: locking on port.count etc */
port->port.count++;
tty->driver_data = port;
tty_port_tty_set(&port->port, tty);
error = isicom_setup_port(tty);
if (error == 0)
error = block_til_ready(tty, filp, port);
error = tty_port_block_til_ready(&port->port, tty, filp);
return error;
}

Expand Down
71 changes: 1 addition & 70 deletions drivers/char/mxser.c
Original file line number Diff line number Diff line change
Expand Up @@ -558,75 +558,6 @@ static void mxser_raise_dtr_rts(struct tty_port *port)
spin_unlock_irqrestore(&mp->slock, flags);
}

static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp,
struct mxser_port *mp)
{
DECLARE_WAITQUEUE(wait, current);
int retval;
int do_clocal = 0;
unsigned long flags;
int cd;
struct tty_port *port = &mp->port;

/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if ((filp->f_flags & O_NONBLOCK) ||
test_bit(TTY_IO_ERROR, &tty->flags)) {
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

if (tty->termios->c_cflag & CLOCAL)
do_clocal = 1;

/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, port->count is dropped by one, so that
* mxser_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&port->open_wait, &wait);

spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->count--;
port->blocked_open++;
spin_unlock_irqrestore(&port->lock, flags);
while (1) {
tty_port_raise_dtr_rts(port);
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
break;
}
cd = tty_port_carrier_raised(port);
if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->open_wait, &wait);
spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->count++;
port->blocked_open--;
if (retval == 0)
port->flags |= ASYNC_NORMAL_ACTIVE;
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}

static int mxser_set_baud(struct tty_struct *tty, long newspd)
{
struct mxser_port *info = tty->driver_data;
Expand Down Expand Up @@ -1110,7 +1041,7 @@ static int mxser_open(struct tty_struct *tty, struct file *filp)
if (retval)
return retval;

retval = mxser_block_til_ready(tty, filp, info);
retval = tty_port_block_til_ready(&info->port, tty, filp);
if (retval)
return retval;

Expand Down
86 changes: 1 addition & 85 deletions drivers/char/riscom8.c
Original file line number Diff line number Diff line change
Expand Up @@ -874,90 +874,6 @@ static int carrier_raised(struct tty_port *port)
return CD;
}

static int block_til_ready(struct tty_struct *tty, struct file *filp,
struct riscom_port *rp)
{
DECLARE_WAITQUEUE(wait, current);
int retval;
int do_clocal = 0;
int CD;
unsigned long flags;
struct tty_port *port = &rp->port;

/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
interruptible_sleep_on(&port->close_wait);
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
}

/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

if (C_CLOCAL(tty))
do_clocal = 1;

/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, info->count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&port->open_wait, &wait);

spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->count--;
port->blocked_open++;
spin_unlock_irqrestore(&port->lock, flags);

while (1) {

CD = tty_port_carrier_raised(port);
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
break;
}
if (!(port->flags & ASYNC_CLOSING) &&
(do_clocal || CD))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&port->open_wait, &wait);
spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->count++;
port->blocked_open--;
if (retval == 0)
port->flags |= ASYNC_NORMAL_ACTIVE;
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}

static int rc_open(struct tty_struct *tty, struct file *filp)
{
int board;
Expand All @@ -984,7 +900,7 @@ static int rc_open(struct tty_struct *tty, struct file *filp)

error = rc_setup_port(bp, port);
if (error == 0)
error = block_til_ready(tty, filp, port);
error = tty_port_block_til_ready(&port->port, tty, filp);
return error;
}

Expand Down
1 change: 1 addition & 0 deletions drivers/char/synclink.c
Original file line number Diff line number Diff line change
Expand Up @@ -3401,6 +3401,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->open_wait, &wait);

/* FIXME: Racy on hangup during close wait */
if (extra_count)
port->count++;
port->blocked_open--;
Expand Down
105 changes: 105 additions & 0 deletions drivers/char/tty_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,108 @@ void tty_port_raise_dtr_rts(struct tty_port *port)
port->ops->raise_dtr_rts(port);
}
EXPORT_SYMBOL(tty_port_raise_dtr_rts);

/**
* tty_port_block_til_ready - Waiting logic for tty open
* @port: the tty port being opened
* @tty: the tty device being bound
* @filp: the file pointer of the opener
*
* Implement the core POSIX/SuS tty behaviour when opening a tty device.
* Handles:
* - hangup (both before and during)
* - non blocking open
* - rts/dtr/dcd
* - signals
* - port flags and counts
*
* The passed tty_port must implement the carrier_raised method if it can
* do carrier detect and the raise_dtr_rts method if it supports software
* management of these lines. Note that the dtr/rts raise is done each
* iteration as a hangup may have previously dropped them while we wait.
*/

int tty_port_block_til_ready(struct tty_port *port,
struct tty_struct *tty, struct file *filp)
{
int do_clocal = 0, retval;
unsigned long flags;
DECLARE_WAITQUEUE(wait, current);
int cd;

/* block if port is in the process of being closed */
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
interruptible_sleep_on(&port->close_wait);
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
}

/* if non-blocking mode is set we can pass directly to open unless
the port has just hung up or is in another error state */
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
port->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}

if (C_CLOCAL(tty))
do_clocal = 1;

/* Block waiting until we can proceed. We may need to wait for the
carrier, but we must also wait for any close that is in progress
before the next open may complete */

retval = 0;
add_wait_queue(&port->open_wait, &wait);

/* The port lock protects the port counts */
spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->count--;
port->blocked_open++;
spin_unlock_irqrestore(&port->lock, flags);

while (1) {
/* Indicate we are open */
tty_port_raise_dtr_rts(port);

set_current_state(TASK_INTERRUPTIBLE);
/* Check for a hangup or uninitialised port. Return accordingly */
if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
break;
}
/* Probe the carrier. For devices with no carrier detect this
will always return true */
cd = tty_port_carrier_raised(port);
if (!(port->flags & ASYNC_CLOSING) &&
(do_clocal || cd))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->open_wait, &wait);

/* Update counts. A parallel hangup will have set count to zero and
we must not mess that up further */
spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->count++;
port->blocked_open--;
if (retval == 0)
port->flags |= ASYNC_NORMAL_ACTIVE;
spin_unlock_irqrestore(&port->lock, flags);
return 0;

}
EXPORT_SYMBOL(tty_port_block_til_ready);

Loading

0 comments on commit 36c621d

Please sign in to comment.