diff options
author | Alan Cox <alan@linux.intel.com> | 2009-10-06 16:06:11 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 15:18:04 -0800 |
commit | 64bc397914265a9ead8d73b63bb31ab3bdd25f67 (patch) | |
tree | dc96d1737d3d70bbdebaea7e0f0aedd1377e1000 | |
parent | 894cb91770f7794f1a17db4df2d83999b197da24 (diff) |
tty_port: add "tty_port_open" helper
For the moment this just moves the USB logic over and fixes the 'what if
we open and hangup at the same time' race noticed by Oliver Neukum.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <oliver@neukum.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/char/tty_port.c | 36 | ||||
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 49 | ||||
-rw-r--r-- | include/linux/tty.h | 10 |
3 files changed, 64 insertions, 31 deletions
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index c63f3d33914a..b22a61a4fbe5 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -99,10 +99,11 @@ EXPORT_SYMBOL(tty_port_tty_set); static void tty_port_shutdown(struct tty_port *port) { + mutex_lock(&port->mutex); if (port->ops->shutdown && test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) port->ops->shutdown(port); - + mutex_unlock(&port->mutex); } /** @@ -381,3 +382,36 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, tty_port_tty_set(port, NULL); } EXPORT_SYMBOL(tty_port_close); + +int tty_port_open(struct tty_port *port, struct tty_struct *tty, + struct file *filp) +{ + spin_lock_irq(&port->lock); + if (!tty_hung_up_p(filp)) + ++port->count; + spin_unlock_irq(&port->lock); + tty_port_tty_set(port, tty); + + /* + * Do the device-specific open only if the hardware isn't + * already initialized. Serialize open and shutdown using the + * port mutex. + */ + + mutex_lock(&port->mutex); + + if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (port->ops->activate) { + int retval = port->ops->activate(port, tty); + if (retval) { + mutex_unlock(&port->mutex); + return retval; + } + } + set_bit(ASYNCB_INITIALIZED, &port->flags); + } + mutex_unlock(&port->mutex); + return tty_port_block_til_ready(port, tty, filp); +} + +EXPORT_SYMBOL(tty_port_open); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index bd3fa7ff15b1..b0649d92251f 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -247,41 +247,31 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) return retval; } -static int serial_open(struct tty_struct *tty, struct file *filp) +static int serial_activate(struct tty_port *tport, struct tty_struct *tty) { - struct usb_serial_port *port = tty->driver_data; + struct usb_serial_port *port = + container_of(tport, struct usb_serial_port, port); struct usb_serial *serial = port->serial; int retval; - dbg("%s - port %d", __func__, port->number); - - spin_lock_irq(&port->port.lock); - if (!tty_hung_up_p(filp)) - ++port->port.count; - spin_unlock_irq(&port->port.lock); - tty_port_tty_set(&port->port, tty); + if (mutex_lock_interruptible(&port->mutex)) + return -ERESTARTSYS; + mutex_lock(&serial->disc_mutex); + if (serial->disconnected) + retval = -ENODEV; + else + retval = port->serial->type->open(tty, port); + mutex_unlock(&serial->disc_mutex); + mutex_unlock(&port->mutex); + return retval; +} - /* Do the device-specific open only if the hardware isn't - * already initialized. - */ - if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) { - if (mutex_lock_interruptible(&port->mutex)) - return -ERESTARTSYS; - mutex_lock(&serial->disc_mutex); - if (serial->disconnected) - retval = -ENODEV; - else - retval = port->serial->type->open(tty, port); - mutex_unlock(&serial->disc_mutex); - mutex_unlock(&port->mutex); - if (retval) - return retval; - set_bit(ASYNCB_INITIALIZED, &port->port.flags); - } +static int serial_open(struct tty_struct *tty, struct file *filp) +{ + struct usb_serial_port *port = tty->driver_data; - /* Now do the correct tty layer semantics */ - retval = tty_port_block_til_ready(&port->port, tty, filp); - return retval; + dbg("%s - port %d", __func__, port->number); + return tty_port_open(&port->port, tty, filp); } /** @@ -725,6 +715,7 @@ static void serial_dtr_rts(struct tty_port *port, int on) static const struct tty_port_operations serial_port_ops = { .carrier_raised = serial_carrier_raised, .dtr_rts = serial_dtr_rts, + .activate = serial_activate, }; int usb_serial_probe(struct usb_interface *interface, diff --git a/include/linux/tty.h b/include/linux/tty.h index f0f43d08d8b8..6352ac257fcb 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -190,9 +190,15 @@ struct tty_port_operations { /* Control the DTR line */ void (*dtr_rts)(struct tty_port *port, int raise); /* Called when the last close completes or a hangup finishes - IFF the port was initialized. Do not use to free resources */ + IFF the port was initialized. Do not use to free resources. Called + under the port mutex to serialize against activate/shutdowns */ void (*shutdown)(struct tty_port *port); void (*drop)(struct tty_port *port); + /* Called under the port mutex from tty_port_open, serialized using + the port mutex */ + /* FIXME: long term getting the tty argument *out* of this would be + good for consoles */ + int (*activate)(struct tty_port *port, struct tty_struct *tty); }; struct tty_port { @@ -467,6 +473,8 @@ extern int tty_port_close_start(struct tty_port *port, extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty); extern void tty_port_close(struct tty_port *port, struct tty_struct *tty, struct file *filp); +extern int tty_port_open(struct tty_port *port, + struct tty_struct *tty, struct file *filp); extern inline int tty_port_users(struct tty_port *port) { return port->count + port->blocked_open; |