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>
This commit is contained in:
parent
c6994e6f06
commit
c706ebdfc8
@ -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)
|
|||||||
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,
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
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 @@
|
|||||||
/* 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 {
|
|||||||
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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user