diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2009-06-02 17:54:11 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 06:44:47 +0200 |
commit | c706ebdfc8955b850e477255a8c0f93f9f14712d (patch) | |
tree | 1f880601cdd2663ee4206783092d5fa9d90c8922 | |
parent | USB: gadget: add USB Audio Gadget driver (diff) | |
download | linux-c706ebdfc8955b850e477255a8c0f93f9f14712d.tar.xz linux-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.c | 27 | ||||
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 24 | ||||
-rw-r--r-- | include/linux/usb/serial.h | 8 |
3 files changed, 41 insertions, 18 deletions
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 83bbb5bca2ef..ba555c528cc6 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 1967a7edc10c..da890f030fac 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 e29ebcf3287b..ed4aa0fa7ed8 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) |