USB: usb-serial: call port_probe and port_remove at the right times
authorAlan Stern <stern@rowland.harvard.edu>
Tue, 2 Jun 2009 15:54:11 +0000 (11:54 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 16 Jun 2009 04:44:47 +0000 (21:44 -0700)
This patch (as1253) prevents the usb-serial core from calling a
driver's port_probe and port_remove methods more than once per port.
It also removes some unnecessary try_module_get() calls and adds a
missing port_remove method call in a failure path.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/bus.c
drivers/usb/serial/usb-serial.c
include/linux/usb/serial.h

index 83bbb5b..ba555c5 100644 (file)
@@ -59,23 +59,22 @@ static int usb_serial_device_probe(struct device *dev)
                retval = -ENODEV;
                goto exit;
        }
+       if (port->dev_state != PORT_REGISTERING)
+               goto exit;
 
        driver = port->serial->type;
        if (driver->port_probe) {
-               if (!try_module_get(driver->driver.owner)) {
-                       dev_err(dev, "module get failed, exiting\n");
-                       retval = -EIO;
-                       goto exit;
-               }
                retval = driver->port_probe(port);
-               module_put(driver->driver.owner);
                if (retval)
                        goto exit;
        }
 
        retval = device_create_file(dev, &dev_attr_port_number);
-       if (retval)
+       if (retval) {
+               if (driver->port_remove)
+                       retval = driver->port_remove(port);
                goto exit;
+       }
 
        minor = port->number;
        tty_register_device(usb_serial_tty_driver, minor, dev);
@@ -98,19 +97,15 @@ static int usb_serial_device_remove(struct device *dev)
        if (!port)
                return -ENODEV;
 
+       if (port->dev_state != PORT_UNREGISTERING)
+               return retval;
+
        device_remove_file(&port->dev, &dev_attr_port_number);
 
        driver = port->serial->type;
-       if (driver->port_remove) {
-               if (!try_module_get(driver->driver.owner)) {
-                       dev_err(dev, "module get failed, exiting\n");
-                       retval = -EIO;
-                       goto exit;
-               }
+       if (driver->port_remove)
                retval = driver->port_remove(port);
-               module_put(driver->driver.owner);
-       }
-exit:
+
        minor = port->number;
        tty_unregister_device(usb_serial_tty_driver, minor);
        dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
index 1967a7e..da890f0 100644 (file)
@@ -1046,10 +1046,15 @@ int usb_serial_probe(struct usb_interface *interface,
 
                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);
-               if (retval)
+               if (retval) {
                        dev_err(&port->dev, "Error registering port device, "
                                "continuing\n");
+                       port->dev_state = PORT_UNREGISTERED;
+               } else {
+                       port->dev_state = PORT_REGISTERED;
+               }
        }
 
        usb_serial_console_init(debug, minor);
@@ -1130,7 +1135,22 @@ void usb_serial_disconnect(struct usb_interface *interface)
                        }
                        kill_traffic(port);
                        cancel_work_sync(&port->work);
-                       device_del(&port->dev);
+                       if (port->dev_state == PORT_REGISTERED) {
+
+                               /* Make sure the port is bound so that the
+                                * driver's port_remove method is called.
+                                */
+                               if (!port->dev.driver) {
+                                       int rc;
+
+                                       port->dev.driver =
+                                                       &serial->type->driver;
+                                       rc = device_bind_driver(&port->dev);
+                               }
+                               port->dev_state = PORT_UNREGISTERING;
+                               device_del(&port->dev);
+                               port->dev_state = PORT_UNREGISTERED;
+                       }
                }
        }
        serial->type->shutdown(serial);
index e29ebcf..ed4aa0f 100644 (file)
 /* parity check flag */
 #define RELEVANT_IFLAG(iflag)  (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
 
+enum port_dev_state {
+       PORT_UNREGISTERED,
+       PORT_REGISTERING,
+       PORT_REGISTERED,
+       PORT_UNREGISTERING,
+};
+
 /**
  * usb_serial_port: structure for the specific ports of a device.
  * @serial: pointer back to the struct usb_serial owner of this port.
@@ -102,6 +109,7 @@ struct usb_serial_port {
        char                    console;
        unsigned long           sysrq; /* sysrq timeout */
        struct device           dev;
+       enum port_dev_state     dev_state;
 };
 #define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev)