Commit c706ebdf authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c6994e6f
...@@ -59,23 +59,22 @@ static int usb_serial_device_probe(struct device *dev) ...@@ -59,23 +59,22 @@ static int usb_serial_device_probe(struct device *dev)
retval = -ENODEV; retval = -ENODEV;
goto exit; goto exit;
} }
if (port->dev_state != PORT_REGISTERING)
goto exit;
driver = port->serial->type; driver = port->serial->type;
if (driver->port_probe) { 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); retval = driver->port_probe(port);
module_put(driver->driver.owner);
if (retval) if (retval)
goto exit; goto exit;
} }
retval = device_create_file(dev, &dev_attr_port_number); retval = device_create_file(dev, &dev_attr_port_number);
if (retval) if (retval) {
if (driver->port_remove)
retval = driver->port_remove(port);
goto exit; goto exit;
}
minor = port->number; minor = port->number;
tty_register_device(usb_serial_tty_driver, minor, dev); tty_register_device(usb_serial_tty_driver, minor, dev);
...@@ -98,19 +97,15 @@ static int usb_serial_device_remove(struct device *dev) ...@@ -98,19 +97,15 @@ static int usb_serial_device_remove(struct device *dev)
if (!port) if (!port)
return -ENODEV; return -ENODEV;
if (port->dev_state != PORT_UNREGISTERING)
return retval;
device_remove_file(&port->dev, &dev_attr_port_number); device_remove_file(&port->dev, &dev_attr_port_number);
driver = port->serial->type; driver = port->serial->type;
if (driver->port_remove) { if (driver->port_remove)
if (!try_module_get(driver->driver.owner)) {
dev_err(dev, "module get failed, exiting\n");
retval = -EIO;
goto exit;
}
retval = driver->port_remove(port); retval = driver->port_remove(port);
module_put(driver->driver.owner);
}
exit:
minor = port->number; minor = port->number;
tty_unregister_device(usb_serial_tty_driver, minor); tty_unregister_device(usb_serial_tty_driver, minor);
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n", dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
......
...@@ -1046,10 +1046,15 @@ int usb_serial_probe(struct usb_interface *interface, ...@@ -1046,10 +1046,15 @@ int usb_serial_probe(struct usb_interface *interface,
dev_set_name(&port->dev, "ttyUSB%d", port->number); dev_set_name(&port->dev, "ttyUSB%d", port->number);
dbg ("%s - registering %s", __func__, dev_name(&port->dev)); dbg ("%s - registering %s", __func__, dev_name(&port->dev));
port->dev_state = PORT_REGISTERING;
retval = device_register(&port->dev); retval = device_register(&port->dev);
if (retval) if (retval) {
dev_err(&port->dev, "Error registering port device, " dev_err(&port->dev, "Error registering port device, "
"continuing\n"); "continuing\n");
port->dev_state = PORT_UNREGISTERED;
} else {
port->dev_state = PORT_REGISTERED;
}
} }
usb_serial_console_init(debug, minor); usb_serial_console_init(debug, minor);
...@@ -1130,7 +1135,22 @@ void usb_serial_disconnect(struct usb_interface *interface) ...@@ -1130,7 +1135,22 @@ void usb_serial_disconnect(struct usb_interface *interface)
} }
kill_traffic(port); kill_traffic(port);
cancel_work_sync(&port->work); 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); serial->type->shutdown(serial);
......
...@@ -27,6 +27,13 @@ ...@@ -27,6 +27,13 @@
/* parity check flag */ /* parity check flag */
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) #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. * usb_serial_port: structure for the specific ports of a device.
* @serial: pointer back to the struct usb_serial owner of this port. * @serial: pointer back to the struct usb_serial owner of this port.
...@@ -102,6 +109,7 @@ struct usb_serial_port { ...@@ -102,6 +109,7 @@ struct usb_serial_port {
char console; char console;
unsigned long sysrq; /* sysrq timeout */ unsigned long sysrq; /* sysrq timeout */
struct device dev; struct device dev;
enum port_dev_state dev_state;
}; };
#define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev) #define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment