tty: Fix various bogus WARN checks in the usb serial layer
[safe/jmp/linux-2.6] / drivers / usb / serial / usb-serial.c
index 9e6027b..3873660 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/smp_lock.h>
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
 #include <linux/mutex.h>
 #include <linux/list.h>
 #include <linux/uaccess.h>
+#include <linux/serial.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
+#include <linux/kfifo.h>
 #include "pl2303.h"
 
 /*
@@ -41,8 +44,6 @@
 #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
 #define DRIVER_DESC "USB Serial Driver core"
 
-static void port_free(struct usb_serial_port *port);
-
 /* Driver structure we register with the USB core */
 static struct usb_driver usb_serial_driver = {
        .name =         "usbserial",
@@ -66,6 +67,11 @@ static struct usb_serial *serial_table[SERIAL_TTY_MINORS];
 static DEFINE_MUTEX(table_lock);
 static LIST_HEAD(usb_serial_driver_list);
 
+/*
+ * Look up the serial structure.  If it is found and it hasn't been
+ * disconnected, return with its disc_mutex held and its refcount
+ * incremented.  Otherwise return NULL.
+ */
 struct usb_serial *usb_serial_get_by_index(unsigned index)
 {
        struct usb_serial *serial;
@@ -73,8 +79,15 @@ struct usb_serial *usb_serial_get_by_index(unsigned index)
        mutex_lock(&table_lock);
        serial = serial_table[index];
 
-       if (serial)
-               kref_get(&serial->kref);
+       if (serial) {
+               mutex_lock(&serial->disc_mutex);
+               if (serial->disconnected) {
+                       mutex_unlock(&serial->disc_mutex);
+                       serial = NULL;
+               } else {
+                       kref_get(&serial->kref);
+               }
+       }
        mutex_unlock(&table_lock);
        return serial;
 }
@@ -123,8 +136,10 @@ static void return_serial(struct usb_serial *serial)
 
        dbg("%s", __func__);
 
+       mutex_lock(&table_lock);
        for (i = 0; i < serial->num_ports; ++i)
                serial_table[serial->minor + i] = NULL;
+       mutex_unlock(&table_lock);
 }
 
 static void destroy_serial(struct kref *kref)
@@ -141,222 +156,197 @@ static void destroy_serial(struct kref *kref)
        if (serial->minor != SERIAL_TTY_NO_MINOR)
                return_serial(serial);
 
-       serial->type->release(serial);
+       if (serial->attached)
+               serial->type->release(serial);
 
-       for (i = 0; i < serial->num_ports; ++i) {
+       /* Now that nothing is using the ports, they can be freed */
+       for (i = 0; i < serial->num_port_pointers; ++i) {
                port = serial->port[i];
-               if (port)
+               if (port) {
+                       port->serial = NULL;
                        put_device(&port->dev);
-       }
-
-       /* If this is a "fake" port, we have to clean it up here, as it will
-        * not get cleaned up in port_release() as it was never registered with
-        * the driver core */
-       if (serial->num_ports < serial->num_port_pointers) {
-               for (i = serial->num_ports;
-                                       i < serial->num_port_pointers; ++i) {
-                       port = serial->port[i];
-                       if (port)
-                               port_free(port);
                }
        }
 
        usb_put_dev(serial->dev);
-
-       /* free up any memory that we allocated */
        kfree(serial);
 }
 
 void usb_serial_put(struct usb_serial *serial)
 {
-       mutex_lock(&table_lock);
        kref_put(&serial->kref, destroy_serial);
-       mutex_unlock(&table_lock);
 }
 
 /*****************************************************************************
  * Driver tty interface functions
  *****************************************************************************/
-static int serial_open (struct tty_struct *tty, struct file *filp)
+
+/**
+ * serial_install - install tty
+ * @driver: the driver (USB in our case)
+ * @tty: the tty being created
+ *
+ * Create the termios objects for this tty.  We use the default
+ * USB serial settings but permit them to be overridden by
+ * serial->type->init_termios.
+ *
+ * This is the first place a new tty gets used.  Hence this is where we
+ * acquire references to the usb_serial structure and the driver module,
+ * where we store a pointer to the port, and where we do an autoresume.
+ * All these actions are reversed in serial_cleanup().
+ */
+static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
 {
+       int idx = tty->index;
        struct usb_serial *serial;
        struct usb_serial_port *port;
-       unsigned int portNumber;
-       int retval = 0;
+       int retval = -ENODEV;
 
        dbg("%s", __func__);
 
-       /* get the serial object associated with this tty pointer */
-       serial = usb_serial_get_by_index(tty->index);
-       if (!serial) {
-               tty->driver_data = NULL;
-               return -ENODEV;
-       }
+       serial = usb_serial_get_by_index(idx);
+       if (!serial)
+               return retval;
 
-       mutex_lock(&serial->disc_mutex);
-       portNumber = tty->index - serial->minor;
-       port = serial->port[portNumber];
-       if (!port || serial->disconnected)
-               retval = -ENODEV;
-       else
-               get_device(&port->dev);
-       /*
-        * Note: Our locking order requirement does not allow port->mutex
-        * to be acquired while serial->disc_mutex is held.
-        */
-       mutex_unlock(&serial->disc_mutex);
+       port = serial->port[idx - serial->minor];
+       if (!port)
+               goto error_no_port;
+       if (!try_module_get(serial->type->driver.owner))
+               goto error_module_get;
+
+       /* perform the standard setup */
+       retval = tty_init_termios(tty);
        if (retval)
-               goto bailout_serial_put;
+               goto error_init_termios;
 
-       if (mutex_lock_interruptible(&port->mutex)) {
-               retval = -ERESTARTSYS;
-               goto bailout_port_put;
-       }
+       retval = usb_autopm_get_interface(serial->interface);
+       if (retval)
+               goto error_get_interface;
+
+       mutex_unlock(&serial->disc_mutex);
 
-       ++port->port.count;
+       /* allow the driver to update the settings */
+       if (serial->type->init_termios)
+               serial->type->init_termios(tty);
 
-       /* set up our port structure making the tty driver
-        * remember our port object, and us it */
        tty->driver_data = port;
-       tty_port_tty_set(&port->port, tty);
 
-       /* If the console is attached, the device is already open */
-       if (port->port.count == 1 && !port->console) {
+       /* Final install (we use the default method) */
+       tty_driver_kref_get(driver);
+       tty->count++;
+       driver->ttys[idx] = tty;
+       return retval;
 
-               /* lock this module before we call it
-                * this may fail, which means we must bail out,
-                * safe because we are called with BKL held */
-               if (!try_module_get(serial->type->driver.owner)) {
-                       retval = -ENODEV;
-                       goto bailout_mutex_unlock;
-               }
+ error_get_interface:
+ error_init_termios:
+       module_put(serial->type->driver.owner);
+ error_module_get:
+ error_no_port:
+       usb_serial_put(serial);
+       mutex_unlock(&serial->disc_mutex);
+       return retval;
+}
 
-               mutex_lock(&serial->disc_mutex);
-               if (serial->disconnected)
-                       retval = -ENODEV;
-               else
-                       retval = usb_autopm_get_interface(serial->interface);
-               if (retval)
-                       goto bailout_module_put;
-
-               /* only call the device specific open if this
-                * is the first time the port is opened */
-               retval = serial->type->open(tty, port, filp);
-               if (retval)
-                       goto bailout_interface_put;
-               mutex_unlock(&serial->disc_mutex);
-       }
-       mutex_unlock(&port->mutex);
-       /* Now do the correct tty layer semantics */
-       retval = tty_port_block_til_ready(&port->port, tty, filp);
-       if (retval == 0)
-               return 0;
+static int serial_activate(struct tty_port *tport, struct tty_struct *tty)
+{
+       struct usb_serial_port *port =
+               container_of(tport, struct usb_serial_port, port);
+       struct usb_serial *serial = port->serial;
+       int retval;
 
-bailout_interface_put:
-       usb_autopm_put_interface(serial->interface);
-bailout_module_put:
+       mutex_lock(&serial->disc_mutex);
+       if (serial->disconnected)
+               retval = -ENODEV;
+       else
+               retval = port->serial->type->open(tty, port);
        mutex_unlock(&serial->disc_mutex);
-       module_put(serial->type->driver.owner);
-bailout_mutex_unlock:
-       port->port.count = 0;
-       tty->driver_data = NULL;
-       tty_port_tty_set(&port->port, NULL);
-       mutex_unlock(&port->mutex);
-bailout_port_put:
-       put_device(&port->dev);
-bailout_serial_put:
-       usb_serial_put(serial);
        return retval;
 }
 
+static int serial_open(struct tty_struct *tty, struct file *filp)
+{
+       struct usb_serial_port *port = tty->driver_data;
+
+       dbg("%s - port %d", __func__, port->number);
+       return tty_port_open(&port->port, tty, filp);
+}
+
 /**
- *     serial_do_down          -       shut down hardware
- *     @port: port to shut down
+ * serial_down - shut down hardware
+ * @tport: tty port to shut down
  *
- *     Shut down a USB port unless it is the console. We never shut down the
- *     console hardware as it will always be in use.
- *
- *     Don't free any resources at this point
+ * Shut down a USB serial port unless it is the console.  We never
+ * shut down the console hardware as it will always be in use. Serialized
+ * against activate by the tport mutex and kept to matching open/close pairs
+ * of calls by the ASYNCB_INITIALIZED flag.
  */
-static void serial_do_down(struct usb_serial_port *port)
+static void serial_down(struct tty_port *tport)
 {
+       struct usb_serial_port *port =
+               container_of(tport, struct usb_serial_port, port);
        struct usb_serial_driver *drv = port->serial->type;
-       struct usb_serial *serial;
-       struct module *owner;
-
-       /* The console is magical, do not hang up the console hardware
-          or there will be tears */
+       /*
+        * The console is magical.  Do not hang up the console hardware
+        * or there will be tears.
+        */
        if (port->console)
                return;
-
-       mutex_lock(&port->mutex);
-       serial = port->serial;
-       owner = serial->type->driver.owner;
-
        if (drv->close)
                drv->close(port);
+}
 
-       mutex_unlock(&port->mutex);
+static void serial_hangup(struct tty_struct *tty)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       dbg("%s - port %d", __func__, port->number);
+       tty_port_hangup(&port->port);
+}
+
+static void serial_close(struct tty_struct *tty, struct file *filp)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       dbg("%s - port %d", __func__, port->number);
+       tty_port_close(&port->port, tty, filp);
 }
 
 /**
- *     serial_do_free          -       free resources post close/hangup
- *     @port: port to free up
+ * serial_cleanup - free resources post close/hangup
+ * @port: port to free up
+ *
+ * Do the resource freeing and refcount dropping for the port.
+ * Avoid freeing the console.
  *
- *     Do the resource freeing and refcount dropping for the port. We must
- *     be careful about ordering and we must avoid freeing up the console.
+ * Called asynchronously after the last tty kref is dropped,
+ * and the tty layer has already done the tty_shutdown(tty);
  */
-
-static void serial_do_free(struct usb_serial_port *port)
+static void serial_cleanup(struct tty_struct *tty)
 {
+       struct usb_serial_port *port = tty->driver_data;
        struct usb_serial *serial;
        struct module *owner;
 
-       /* The console is magical, do not hang up the console hardware
-          or there will be tears */
+       /* The console is magical.  Do not hang up the console hardware
+        * or there will be tears.
+        */
        if (port->console)
                return;
 
+       dbg("%s - port %d", __func__, port->number);
+
+       tty->driver_data = NULL;
+
        serial = port->serial;
        owner = serial->type->driver.owner;
-       put_device(&port->dev);
-       /* Mustn't dereference port any more */
+
        mutex_lock(&serial->disc_mutex);
        if (!serial->disconnected)
                usb_autopm_put_interface(serial->interface);
        mutex_unlock(&serial->disc_mutex);
+
        usb_serial_put(serial);
-       /* Mustn't dereference serial any more */
        module_put(owner);
 }
 
-static void serial_close(struct tty_struct *tty, struct file *filp)
-{
-       struct usb_serial_port *port = tty->driver_data;
-
-       if (!port)
-               return;
-
-       dbg("%s - port %d", __func__, port->number);
-
-
-       if (tty_port_close_start(&port->port, tty, filp) == 0)
-               return;
-
-       serial_do_down(port);           
-       tty_port_close_end(&port->port, tty);
-       tty_port_tty_set(&port->port, NULL);
-       serial_do_free(port);
-}
-
-static void serial_hangup(struct tty_struct *tty)
-{
-       struct usb_serial_port *port = tty->driver_data;
-       serial_do_down(port);
-       tty_port_hangup(&port->port);
-       serial_do_free(port);
-}
-
 static int serial_write(struct tty_struct *tty, const unsigned char *buf,
                                                                int count)
 {
@@ -368,10 +358,6 @@ static int serial_write(struct tty_struct *tty, const unsigned char *buf,
 
        dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
 
-       /* count is managed under the mutex lock for the tty so cannot
-          drop to zero until after the last close completes */
-       WARN_ON(!port->port.count);
-
        /* pass on to the driver specific version of this function */
        retval = port->serial->type->write(tty, port, buf, count);
 
@@ -383,7 +369,6 @@ static int serial_write_room(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
        dbg("%s - port %d", __func__, port->number);
-       WARN_ON(!port->port.count);
        /* pass on to the driver specific version of this function */
        return port->serial->type->write_room(tty);
 }
@@ -391,9 +376,8 @@ static int serial_write_room(struct tty_struct *tty)
 static int serial_chars_in_buffer(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
-       dbg("%s = port %d", __func__, port->number);
+       dbg("%s - port %d", __func__, port->number);
 
-       WARN_ON(!port->port.count);
        /* if the device was unplugged then any remaining characters
           fell out of the connector ;) */
        if (port->serial->disconnected)
@@ -407,7 +391,6 @@ static void serial_throttle(struct tty_struct *tty)
        struct usb_serial_port *port = tty->driver_data;
        dbg("%s - port %d", __func__, port->number);
 
-       WARN_ON(!port->port.count);
        /* pass on to the driver specific version of this function */
        if (port->serial->type->throttle)
                port->serial->type->throttle(tty);
@@ -418,7 +401,6 @@ static void serial_unthrottle(struct tty_struct *tty)
        struct usb_serial_port *port = tty->driver_data;
        dbg("%s - port %d", __func__, port->number);
 
-       WARN_ON(!port->port.count);
        /* pass on to the driver specific version of this function */
        if (port->serial->type->unthrottle)
                port->serial->type->unthrottle(tty);
@@ -432,8 +414,6 @@ static int serial_ioctl(struct tty_struct *tty, struct file *file,
 
        dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);
 
-       WARN_ON(!port->port.count);
-
        /* pass on to the driver specific version of this function
           if it is available */
        if (port->serial->type->ioctl) {
@@ -448,7 +428,6 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old)
        struct usb_serial_port *port = tty->driver_data;
        dbg("%s - port %d", __func__, port->number);
 
-       WARN_ON(!port->port.count);
        /* pass on to the driver specific version of this function
           if it is available */
        if (port->serial->type->set_termios)
@@ -463,7 +442,6 @@ static int serial_break(struct tty_struct *tty, int break_state)
 
        dbg("%s - port %d", __func__, port->number);
 
-       WARN_ON(!port->port.count);
        /* pass on to the driver specific version of this function
           if it is available */
        if (port->serial->type->break_ctl)
@@ -500,6 +478,7 @@ static int serial_proc_show(struct seq_file *m, void *v)
 
                seq_putc(m, '\n');
                usb_serial_put(serial);
+               mutex_unlock(&serial->disc_mutex);
        }
        return 0;
 }
@@ -523,7 +502,6 @@ static int serial_tiocmget(struct tty_struct *tty, struct file *file)
 
        dbg("%s - port %d", __func__, port->number);
 
-       WARN_ON(!port->port.count);
        if (port->serial->type->tiocmget)
                return port->serial->type->tiocmget(tty, file);
        return -EINVAL;
@@ -536,7 +514,6 @@ static int serial_tiocmset(struct tty_struct *tty, struct file *file,
 
        dbg("%s - port %d", __func__, port->number);
 
-       WARN_ON(!port->port.count);
        if (port->serial->type->tiocmset)
                return port->serial->type->tiocmset(tty, file, set, clear);
        return -EINVAL;
@@ -569,14 +546,6 @@ static void usb_serial_port_work(struct work_struct *work)
        tty_kref_put(tty);
 }
 
-static void port_release(struct device *dev)
-{
-       struct usb_serial_port *port = to_usb_serial_port(dev);
-
-       dbg ("%s - %s", __func__, dev_name(dev));
-       port_free(port);
-}
-
 static void kill_traffic(struct usb_serial_port *port)
 {
        usb_kill_urb(port->read_urb);
@@ -596,8 +565,12 @@ static void kill_traffic(struct usb_serial_port *port)
        usb_kill_urb(port->interrupt_out_urb);
 }
 
-static void port_free(struct usb_serial_port *port)
+static void port_release(struct device *dev)
 {
+       struct usb_serial_port *port = to_usb_serial_port(dev);
+
+       dbg ("%s - %s", __func__, dev_name(dev));
+
        /*
         * Stop all the traffic before cancelling the work, so that
         * nobody will restart it by calling usb_serial_port_softint.
@@ -609,6 +582,7 @@ static void port_free(struct usb_serial_port *port)
        usb_free_urb(port->write_urb);
        usb_free_urb(port->interrupt_in_urb);
        usb_free_urb(port->interrupt_out_urb);
+       kfifo_free(&port->write_fifo);
        kfree(port->bulk_in_buffer);
        kfree(port->bulk_out_buffer);
        kfree(port->interrupt_in_buffer);
@@ -707,6 +681,8 @@ 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,
+       .shutdown = serial_down,
 };
 
 int usb_serial_probe(struct usb_interface *interface,
@@ -905,9 +881,15 @@ int usb_serial_probe(struct usb_interface *interface,
                port->port.ops = &serial_port_ops;
                port->serial = serial;
                spin_lock_init(&port->lock);
-               mutex_init(&port->mutex);
+               /* Keep this for private driver use for the moment but
+                  should probably go away */
                INIT_WORK(&port->work, usb_serial_port_work);
                serial->port[i] = port;
+               port->dev.parent = &interface->dev;
+               port->dev.driver = NULL;
+               port->dev.bus = &usb_serial_bus_type;
+               port->dev.release = &port_release;
+               device_initialize(&port->dev);
        }
 
        /* set up the endpoint information */
@@ -943,6 +925,8 @@ int usb_serial_probe(struct usb_interface *interface,
                        dev_err(&interface->dev, "No free urbs available\n");
                        goto probe_error;
                }
+               if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
+                       goto probe_error;
                buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
                port->bulk_out_size = buffer_size;
                port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
@@ -1033,12 +1017,15 @@ int usb_serial_probe(struct usb_interface *interface,
                module_put(type->driver.owner);
                if (retval < 0)
                        goto probe_error;
+               serial->attached = 1;
                if (retval > 0) {
                        /* quietly accept this device, but don't bind to a
                           serial port as it's about to disappear */
                        serial->num_ports = 0;
                        goto exit;
                }
+       } else {
+               serial->attached = 1;
        }
 
        if (get_free_serial(serial, num_ports, &minor) == NULL) {
@@ -1050,15 +1037,10 @@ int usb_serial_probe(struct usb_interface *interface,
        /* register all of the individual ports with the driver core */
        for (i = 0; i < num_ports; ++i) {
                port = serial->port[i];
-               port->dev.parent = &interface->dev;
-               port->dev.driver = NULL;
-               port->dev.bus = &usb_serial_bus_type;
-               port->dev.release = &port_release;
-
                dev_set_name(&port->dev, "ttyUSB%d", port->number);
                dbg ("%s - registering %s", __func__, dev_name(&port->dev));
                port->dev_state = PORT_REGISTERING;
-               retval = device_register(&port->dev);
+               retval = device_add(&port->dev);
                if (retval) {
                        dev_err(&port->dev, "Error registering port device, "
                                "continuing\n");
@@ -1076,39 +1058,7 @@ exit:
        return 0;
 
 probe_error:
-       for (i = 0; i < num_bulk_in; ++i) {
-               port = serial->port[i];
-               if (!port)
-                       continue;
-               usb_free_urb(port->read_urb);
-               kfree(port->bulk_in_buffer);
-       }
-       for (i = 0; i < num_bulk_out; ++i) {
-               port = serial->port[i];
-               if (!port)
-                       continue;
-               usb_free_urb(port->write_urb);
-               kfree(port->bulk_out_buffer);
-       }
-       for (i = 0; i < num_interrupt_in; ++i) {
-               port = serial->port[i];
-               if (!port)
-                       continue;
-               usb_free_urb(port->interrupt_in_urb);
-               kfree(port->interrupt_in_buffer);
-       }
-       for (i = 0; i < num_interrupt_out; ++i) {
-               port = serial->port[i];
-               if (!port)
-                       continue;
-               usb_free_urb(port->interrupt_out_urb);
-               kfree(port->interrupt_out_buffer);
-       }
-
-       /* free up any memory that we allocated */
-       for (i = 0; i < serial->num_port_pointers; ++i)
-               kfree(serial->port[i]);
-       kfree(serial);
+       usb_serial_put(serial);
        return -EIO;
 }
 EXPORT_SYMBOL_GPL(usb_serial_probe);
@@ -1134,10 +1084,7 @@ void usb_serial_disconnect(struct usb_interface *interface)
                if (port) {
                        struct tty_struct *tty = tty_port_tty_get(&port->port);
                        if (tty) {
-                               /* The hangup will occur asynchronously but
-                                  the object refcounts will sort out all the
-                                  cleanup */
-                               tty_hangup(tty);
+                               tty_vhangup(tty);
                                tty_kref_put(tty);
                        }
                        kill_traffic(port);
@@ -1162,8 +1109,7 @@ void usb_serial_disconnect(struct usb_interface *interface)
        }
        serial->type->disconnect(serial);
 
-       /* let the last holder of this object
-        * cause it to be cleaned up */
+       /* let the last holder of this object cause it to be cleaned up */
        usb_serial_put(serial);
        dev_info(dev, "device disconnected\n");
 }
@@ -1177,15 +1123,21 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
 
        serial->suspending = 1;
 
+       if (serial->type->suspend) {
+               r = serial->type->suspend(serial, message);
+               if (r < 0) {
+                       serial->suspending = 0;
+                       goto err_out;
+               }
+       }
+
        for (i = 0; i < serial->num_ports; ++i) {
                port = serial->port[i];
                if (port)
                        kill_traffic(port);
        }
 
-       if (serial->type->suspend)
-               r = serial->type->suspend(serial, message);
-
+err_out:
        return r;
 }
 EXPORT_SYMBOL(usb_serial_suspend);
@@ -1219,6 +1171,8 @@ static const struct tty_operations serial_ops = {
        .chars_in_buffer =      serial_chars_in_buffer,
        .tiocmget =             serial_tiocmget,
        .tiocmset =             serial_tiocmset,
+       .cleanup =              serial_cleanup,
+       .install =              serial_install,
        .proc_fops =            &serial_proc_fops,
 };