kfifo: move struct kfifo in place
[safe/jmp/linux-2.6] / drivers / usb / serial / usb-serial.c
index 87802ea..44b72d4 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/serial.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
+#include <linux/kfifo.h>
 #include "pl2303.h"
 
 /*
@@ -155,7 +156,8 @@ 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);
 
        /* Now that nothing is using the ports, they can be freed */
        for (i = 0; i < serial->num_port_pointers; ++i) {
@@ -187,197 +189,137 @@ void usb_serial_put(struct usb_serial *serial)
  * 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;
-       int retval;
-
-       /* If the termios setup has yet to be done */
-       if (tty->driver->termios[idx] == NULL) {
-               /* perform the standard setup */
-               retval = tty_init_termios(tty);
-               if (retval)
-                       return retval;
-               /* allow the driver to update it */
-               serial = usb_serial_get_by_index(tty->index);
-               if (serial) {
-                       if (serial->type->init_termios)
-                               serial->type->init_termios(tty);
-                       usb_serial_put(serial);
-                       mutex_unlock(&serial->disc_mutex);
-               }
-       }
-       /* Final install (we use the default method) */
-       tty_driver_kref_get(driver);
-       tty->count++;
-       driver->ttys[idx] = tty;
-       return 0;
-}
-
-static int serial_open (struct tty_struct *tty, struct file *filp)
-{
-       struct usb_serial *serial;
        struct usb_serial_port *port;
-       unsigned int portNumber;
-       int retval = 0;
-       int first = 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;
 
-       portNumber = tty->index - serial->minor;
-       port = serial->port[portNumber];
-       if (!port || serial->disconnected)
-               retval = -ENODEV;
-       /*
-        * 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_serial_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) {
-               first = 1;
-               /* 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;
-               }
 
-               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);
-               if (retval)
-                       goto bailout_interface_put;
-               mutex_unlock(&serial->disc_mutex);
-               set_bit(ASYNCB_INITIALIZED, &port->port.flags);
-       }
-       mutex_unlock(&port->mutex);
-       /* Now do the correct tty layer semantics */
-       retval = tty_port_block_til_ready(&port->port, tty, filp);
-       if (retval == 0) {
-               if (!first)
-                       usb_serial_put(serial);
-               return 0;
-       }
-       mutex_lock(&port->mutex);
-       if (first == 0)
-               goto bailout_mutex_unlock;
-       /* Undo the initial port actions */
-       mutex_lock(&serial->disc_mutex);
-bailout_interface_put:
-       usb_autopm_put_interface(serial->interface);
-bailout_module_put:
-       mutex_unlock(&serial->disc_mutex);
+       /* Final install (we use the default method) */
+       tty_driver_kref_get(driver);
+       tty->count++;
+       driver->ttys[idx] = tty;
+       return retval;
+
+ error_get_interface:
+ error_init_termios:
        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_serial_put:
+ error_module_get:
+ error_no_port:
        usb_serial_put(serial);
+       mutex_unlock(&serial->disc_mutex);
        return retval;
 }
 
+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;
+
+       mutex_lock(&serial->disc_mutex);
+       if (serial->disconnected)
+               retval = -ENODEV;
+       else
+               retval = port->serial->type->open(tty, port);
+       mutex_unlock(&serial->disc_mutex);
+       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 serial port unless it is the console.  We never
- * shut down the console hardware as it will always be in use.
+ * 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.
         */
        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;
-       serial_do_down(port);
+       dbg("%s - port %d", __func__, port->number);
        tty_port_hangup(&port->port);
-       /* We must not free port yet - the USB serial layer depends on it's
-          continued existence */
 }
 
 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);
-
+       tty_port_close(&port->port, tty, filp);
 }
 
 /**
- * serial_do_free - free resources post close/hangup
+ * 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.
  *
- * Called when the last tty kref is dropped.
+ * 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 tty_struct *tty)
+static void serial_cleanup(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
        struct usb_serial *serial;
@@ -386,9 +328,13 @@ static void serial_do_free(struct tty_struct *tty)
        /* The console is magical.  Do not hang up the console hardware
         * or there will be tears.
         */
-       if (port == NULL || port->console)
+       if (port->console)
                return;
 
+       dbg("%s - port %d", __func__, port->number);
+
+       tty->driver_data = NULL;
+
        serial = port->serial;
        owner = serial->type->driver.owner;
 
@@ -649,6 +595,8 @@ static void port_release(struct device *dev)
        usb_free_urb(port->write_urb);
        usb_free_urb(port->interrupt_in_urb);
        usb_free_urb(port->interrupt_out_urb);
+       if (!IS_ERR(port->write_fifo) && port->write_fifo)
+               kfifo_free(port->write_fifo);
        kfree(port->bulk_in_buffer);
        kfree(port->bulk_out_buffer);
        kfree(port->interrupt_in_buffer);
@@ -747,6 +695,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,
@@ -945,7 +895,8 @@ 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;
@@ -988,6 +939,9 @@ 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,
+                       &port->lock))
+                       goto probe_error;
                buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
                port->bulk_out_size = buffer_size;
                port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
@@ -1078,12 +1032,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) {
@@ -1181,15 +1138,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);
@@ -1223,7 +1186,7 @@ static const struct tty_operations serial_ops = {
        .chars_in_buffer =      serial_chars_in_buffer,
        .tiocmget =             serial_tiocmget,
        .tiocmset =             serial_tiocmset,
-       .shutdown =             serial_do_free,
+       .cleanup =              serial_cleanup,
        .install =              serial_install,
        .proc_fops =            &serial_proc_fops,
 };