summaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial/usb-serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/serial/usb-serial.c')
-rw-r--r--drivers/usb/serial/usb-serial.c62
1 files changed, 39 insertions, 23 deletions
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 9c36f0ece20..b59a0536ea5 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -16,7 +16,6 @@
*
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
@@ -162,12 +161,19 @@ static void destroy_serial(struct kref *kref)
}
}
+ flush_scheduled_work(); /* port->work */
+
usb_put_dev(serial->dev);
/* free up any memory that we allocated */
kfree (serial);
}
+void usb_serial_put(struct usb_serial *serial)
+{
+ kref_put(&serial->kref, destroy_serial);
+}
+
/*****************************************************************************
* Driver tty interface functions
*****************************************************************************/
@@ -201,12 +207,12 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
++port->open_count;
- if (port->open_count == 1) {
+ /* set up our port structure making the tty driver
+ * remember our port object, and us it */
+ tty->driver_data = port;
+ port->tty = tty;
- /* set up our port structure making the tty driver
- * remember our port object, and us it */
- tty->driver_data = port;
- port->tty = tty;
+ if (port->open_count == 1) {
/* lock this module before we call it
* this may fail, which means we must bail out,
@@ -230,9 +236,11 @@ bailout_module_put:
module_put(serial->type->driver.owner);
bailout_mutex_unlock:
port->open_count = 0;
+ tty->driver_data = NULL;
+ port->tty = NULL;
mutex_unlock(&port->mutex);
bailout_kref_put:
- kref_put(&serial->kref, destroy_serial);
+ usb_serial_put(serial);
return retval;
}
@@ -268,7 +276,7 @@ static void serial_close(struct tty_struct *tty, struct file * filp)
}
mutex_unlock(&port->mutex);
- kref_put(&port->serial->kref, destroy_serial);
+ usb_serial_put(port->serial);
}
static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count)
@@ -276,7 +284,7 @@ static int serial_write (struct tty_struct * tty, const unsigned char *buf, int
struct usb_serial_port *port = tty->driver_data;
int retval = -EINVAL;
- if (!port)
+ if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED)
goto exit;
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
@@ -296,7 +304,7 @@ exit:
static int serial_write_room (struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- int retval = -EINVAL;
+ int retval = -ENODEV;
if (!port)
goto exit;
@@ -318,7 +326,7 @@ exit:
static int serial_chars_in_buffer (struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- int retval = -EINVAL;
+ int retval = -ENODEV;
if (!port)
goto exit;
@@ -473,7 +481,7 @@ static int serial_read_proc (char *page, char **start, off_t off, int count, int
begin += length;
length = 0;
}
- kref_put(&serial->kref, destroy_serial);
+ usb_serial_put(serial);
}
*eof = 1;
done:
@@ -488,19 +496,18 @@ static int serial_tiocmget (struct tty_struct *tty, struct file *file)
struct usb_serial_port *port = tty->driver_data;
if (!port)
- goto exit;
+ return -ENODEV;
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return -ENODEV;
}
if (port->serial->type->tiocmget)
return port->serial->type->tiocmget(port, file);
-exit:
return -EINVAL;
}
@@ -510,23 +517,32 @@ static int serial_tiocmset (struct tty_struct *tty, struct file *file,
struct usb_serial_port *port = tty->driver_data;
if (!port)
- goto exit;
+ return -ENODEV;
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return -ENODEV;
}
if (port->serial->type->tiocmset)
return port->serial->type->tiocmset(port, file, set, clear);
-exit:
return -EINVAL;
}
-void usb_serial_port_softint(void *private)
+/*
+ * We would be calling tty_wakeup here, but unfortunately some line
+ * disciplines have an annoying habit of calling tty->write from
+ * the write wakeup callback (e.g. n_hdlc.c).
+ */
+void usb_serial_port_softint(struct usb_serial_port *port)
+{
+ schedule_work(&port->work);
+}
+
+static void usb_serial_port_work(void *private)
{
struct usb_serial_port *port = private;
struct tty_struct *tty;
@@ -789,7 +805,7 @@ int usb_serial_probe(struct usb_interface *interface,
port->serial = serial;
spin_lock_init(&port->lock);
mutex_init(&port->mutex);
- INIT_WORK(&port->work, usb_serial_port_softint, port);
+ INIT_WORK(&port->work, usb_serial_port_work, port);
serial->port[i] = port;
}
@@ -985,6 +1001,7 @@ void usb_serial_disconnect(struct usb_interface *interface)
struct device *dev = &interface->dev;
struct usb_serial_port *port;
+ usb_serial_console_disconnect(serial);
dbg ("%s", __FUNCTION__);
usb_set_intfdata (interface, NULL);
@@ -996,7 +1013,7 @@ void usb_serial_disconnect(struct usb_interface *interface)
}
/* let the last holder of this object
* cause it to be cleaned up */
- kref_put(&serial->kref, destroy_serial);
+ usb_serial_put(serial);
}
dev_info(dev, "device disconnected\n");
}
@@ -1041,13 +1058,12 @@ static int __init usb_serial_init(void)
usb_serial_tty_driver->owner = THIS_MODULE;
usb_serial_tty_driver->driver_name = "usbserial";
- usb_serial_tty_driver->devfs_name = "usb/tts/";
usb_serial_tty_driver->name = "ttyUSB";
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
usb_serial_tty_driver->minor_start = 0;
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
- usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+ usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
usb_serial_tty_driver->init_termios = tty_std_termios;
usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(usb_serial_tty_driver, &serial_ops);