USB: serial: fix race between probe and open
[linux-flexiantxendom0.git] / drivers / usb / serial / usb-serial.c
index 6954de5..38d7ebd 100644 (file)
@@ -406,7 +406,7 @@ static void serial_unthrottle(struct tty_struct *tty)
                port->serial->type->unthrottle(tty);
 }
 
-static int serial_ioctl(struct tty_struct *tty, struct file *file,
+static int serial_ioctl(struct tty_struct *tty,
                                        unsigned int cmd, unsigned long arg)
 {
        struct usb_serial_port *port = tty->driver_data;
@@ -417,7 +417,7 @@ static int serial_ioctl(struct tty_struct *tty, struct file *file,
        /* pass on to the driver specific version of this function
           if it is available */
        if (port->serial->type->ioctl) {
-               retval = port->serial->type->ioctl(tty, file, cmd, arg);
+               retval = port->serial->type->ioctl(tty, cmd, arg);
        } else
                retval = -ENOIOCTLCMD;
        return retval;
@@ -496,18 +496,18 @@ static const struct file_operations serial_proc_fops = {
        .release        = single_release,
 };
 
-static int serial_tiocmget(struct tty_struct *tty, struct file *file)
+static int serial_tiocmget(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
 
        dbg("%s - port %d", __func__, port->number);
 
        if (port->serial->type->tiocmget)
-               return port->serial->type->tiocmget(tty, file);
+               return port->serial->type->tiocmget(tty);
        return -EINVAL;
 }
 
-static int serial_tiocmset(struct tty_struct *tty, struct file *file,
+static int serial_tiocmset(struct tty_struct *tty,
                            unsigned int set, unsigned int clear)
 {
        struct usb_serial_port *port = tty->driver_data;
@@ -515,7 +515,7 @@ static int serial_tiocmset(struct tty_struct *tty, struct file *file,
        dbg("%s - port %d", __func__, port->number);
 
        if (port->serial->type->tiocmset)
-               return port->serial->type->tiocmset(tty, file, set, clear);
+               return port->serial->type->tiocmset(tty, set, clear);
        return -EINVAL;
 }
 
@@ -911,9 +911,8 @@ int usb_serial_probe(struct usb_interface *interface,
                        dev_err(&interface->dev, "No free urbs available\n");
                        goto probe_error;
                }
-               buffer_size = serial->type->bulk_in_size;
-               if (!buffer_size)
-                       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+               buffer_size = max_t(int, serial->type->bulk_in_size,
+                               usb_endpoint_maxp(endpoint));
                port->bulk_in_size = buffer_size;
                port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
                port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
@@ -943,7 +942,7 @@ int usb_serial_probe(struct usb_interface *interface,
                        goto probe_error;
                buffer_size = serial->type->bulk_out_size;
                if (!buffer_size)
-                       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+                       buffer_size = usb_endpoint_maxp(endpoint);
                port->bulk_out_size = buffer_size;
                port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
                port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
@@ -991,7 +990,7 @@ int usb_serial_probe(struct usb_interface *interface,
                                                "No free urbs available\n");
                                goto probe_error;
                        }
-                       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+                       buffer_size = usb_endpoint_maxp(endpoint);
                        port->interrupt_in_endpointAddress =
                                                endpoint->bEndpointAddress;
                        port->interrupt_in_buffer = kmalloc(buffer_size,
@@ -1022,7 +1021,7 @@ int usb_serial_probe(struct usb_interface *interface,
                                                "No free urbs available\n");
                                goto probe_error;
                        }
-                       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+                       buffer_size = usb_endpoint_maxp(endpoint);
                        port->interrupt_out_size = buffer_size;
                        port->interrupt_out_endpointAddress =
                                                endpoint->bEndpointAddress;
@@ -1060,6 +1059,12 @@ int usb_serial_probe(struct usb_interface *interface,
                serial->attached = 1;
        }
 
+       /* Avoid race with tty_open and serial_install by setting the
+        * disconnected flag and not clearing it until all ports have been
+        * registered.
+        */
+       serial->disconnected = 1;
+
        if (get_free_serial(serial, num_ports, &minor) == NULL) {
                dev_err(&interface->dev, "No more free serial devices\n");
                goto probe_error;
@@ -1084,6 +1089,8 @@ int usb_serial_probe(struct usb_interface *interface,
                }
        }
 
+       serial->disconnected = 0;
+
        usb_serial_console_init(debug, minor);
 
 exit:
@@ -1344,11 +1351,15 @@ int usb_serial_register(struct usb_serial_driver *driver)
                return -ENODEV;
 
        fixup_generic(driver);
-       if (driver->usb_driver)
-               driver->usb_driver->supports_autosuspend = 1;
 
        if (!driver->description)
                driver->description = driver->driver.name;
+       if (!driver->usb_driver) {
+               WARN(1, "Serial driver %s has no usb_driver\n",
+                               driver->description);
+               return -EINVAL;
+       }
+       driver->usb_driver->supports_autosuspend = 1;
 
        /* Add this device to our list of devices */
        mutex_lock(&table_lock);