summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2009-06-02 11:54:11 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-15 21:44:47 -0700
commitc706ebdfc8955b850e477255a8c0f93f9f14712d (patch)
tree1f880601cdd2663ee4206783092d5fa9d90c8922
parentc6994e6f067cf0fc4c6cca3d164018b1150916f8 (diff)
downloadlinux-3.10-c706ebdfc8955b850e477255a8c0f93f9f14712d.tar.gz
linux-3.10-c706ebdfc8955b850e477255a8c0f93f9f14712d.tar.bz2
linux-3.10-c706ebdfc8955b850e477255a8c0f93f9f14712d.zip
USB: usb-serial: call port_probe and port_remove at the right times
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>
-rw-r--r--drivers/usb/serial/bus.c27
-rw-r--r--drivers/usb/serial/usb-serial.c24
-rw-r--r--include/linux/usb/serial.h8
3 files changed, 41 insertions, 18 deletions
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 83bbb5bca2e..ba555c528cc 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -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",
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 1967a7edc10..da890f030fa 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -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);
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
index e29ebcf3287..ed4aa0fa7ed 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -27,6 +27,13 @@
/* 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)