intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
+ intf->needs_binding = 0;
if (udev->authorized == 0) {
dev_err(&intf->dev, "Device is not authorized for usage\n");
dev->driver = &driver->drvwrap.driver;
usb_set_intfdata(iface, priv);
+ iface->needs_binding = 0;
usb_pm_lock(udev);
iface->condition = USB_INTERFACE_BOUND;
}
EXPORT_SYMBOL_GPL(usb_deregister);
+
+/* Forced unbinding of a USB interface driver, either because
+ * it doesn't support pre_reset/post_reset/reset_resume or
+ * because it doesn't support suspend/resume.
+ *
+ * The caller must hold @intf's device's lock, but not its pm_mutex
+ * and not @intf->dev.sem.
+ */
+void usb_forced_unbind_intf(struct usb_interface *intf)
+{
+ struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+
+ dev_dbg(&intf->dev, "forced unbind\n");
+ usb_driver_release_interface(driver, intf);
+
+ /* Mark the interface for later rebinding */
+ intf->needs_binding = 1;
+}
+
+/* Delayed forced unbinding of a USB interface driver and scan
+ * for rebinding.
+ *
+ * The caller must hold @intf's device's lock, but not its pm_mutex
+ * and not @intf->dev.sem.
+ *
+ * FIXME: The caller must block system sleep transitions.
+ */
+void usb_rebind_intf(struct usb_interface *intf)
+{
+ int rc;
+
+ /* Delayed unbind of an existing driver */
+ if (intf->dev.driver) {
+ struct usb_driver *driver =
+ to_usb_driver(intf->dev.driver);
+
+ dev_dbg(&intf->dev, "forced unbind\n");
+ usb_driver_release_interface(driver, intf);
+ }
+
+ /* Try to rebind the interface */
+ intf->needs_binding = 0;
+ rc = device_attach(&intf->dev);
+ if (rc < 0)
+ dev_warn(&intf->dev, "rebind failed: %d\n", rc);
+}
+
+#define DO_UNBIND 0
+#define DO_REBIND 1
+
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
+ * or rebind interfaces that have been unbound, according to @action.
+ *
+ * The caller must hold @udev's device lock.
+ * FIXME: For rebinds, the caller must block system sleep transitions.
+ */
+static void do_unbind_rebind(struct usb_device *udev, int action)
+{
+ struct usb_host_config *config;
+ int i;
+ struct usb_interface *intf;
+ struct usb_driver *drv;
+
+ config = udev->actconfig;
+ if (config) {
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ intf = config->interface[i];
+ switch (action) {
+ case DO_UNBIND:
+ if (intf->dev.driver) {
+ drv = to_usb_driver(intf->dev.driver);
+ if (!drv->suspend || !drv->resume)
+ usb_forced_unbind_intf(intf);
+ }
+ break;
+ case DO_REBIND:
+ if (intf->needs_binding) {
+
+ /* FIXME: The next line is needed because we are going to probe
+ * the interface, but as far as the PM core is concerned the
+ * interface is still suspended. The problem wouldn't exist
+ * if we could rebind the interface during the interface's own
+ * resume() call, but at the time the usb_device isn't locked!
+ *
+ * The real solution will be to carry this out during the device's
+ * complete() callback. Until that is implemented, we have to
+ * use this hack.
+ */
+// intf->dev.power.sleeping = 0;
+
+ usb_rebind_intf(intf);
+ }
+ break;
+ }
+ }
+ }
+}
+
#ifdef CONFIG_PM
/* Caller has locked udev's pm_mutex */
goto done;
driver = to_usb_driver(intf->dev.driver);
- if (driver->suspend && driver->resume) {
+ if (driver->suspend) {
status = driver->suspend(intf, msg);
if (status == 0)
mark_quiesced(intf);
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
} else {
- /*
- * FIXME else if there's no suspend method, disconnect...
- * Not possible if auto_pm is set...
- */
- dev_warn(&intf->dev, "no suspend for driver %s?\n",
- driver->name);
+ /* Later we will unbind the driver and reprobe */
+ intf->needs_binding = 1;
+ dev_warn(&intf->dev, "no %s for driver %s?\n",
+ "suspend", driver->name);
mark_quiesced(intf);
}
goto done;
/* Can't resume it if it doesn't have a driver. */
- if (intf->condition == USB_INTERFACE_UNBOUND) {
- status = -ENOTCONN;
+ if (intf->condition == USB_INTERFACE_UNBOUND)
+ goto done;
+
+ /* Don't resume if the interface is marked for rebinding */
+ if (intf->needs_binding)
goto done;
- }
driver = to_usb_driver(intf->dev.driver);
if (reset_resume) {
dev_err(&intf->dev, "%s error %d\n",
"reset_resume", status);
} else {
- /* status = -EOPNOTSUPP; */
+ intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"reset_resume", driver->name);
}
dev_err(&intf->dev, "%s error %d\n",
"resume", status);
} else {
- /* status = -EOPNOTSUPP; */
+ intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"resume", driver->name);
}
done:
dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status);
- if (status == 0)
+ if (status == 0 && intf->condition == USB_INTERFACE_BOUND)
mark_active(intf);
- /* FIXME: Unbind the driver and reprobe if the resume failed
- * (not possible if auto_pm is set) */
+ /* Later we will unbind the driver and/or reprobe, if necessary */
return status;
}
{
int status;
+ do_unbind_rebind(udev, DO_UNBIND);
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_suspend_both(udev, msg);
status = usb_resume_both(udev);
udev->last_busy = jiffies;
usb_pm_unlock(udev);
+ do_unbind_rebind(udev, DO_REBIND);
/* Now that the device is awake, we can start trying to autosuspend
* it again. */
* this from a driver probe() routine after downloading new firmware.
* For calls that might not occur during probe(), drivers should lock
* the device using usb_lock_device_for_reset().
+ *
+ * If an interface is currently being probed or disconnected, we assume
+ * its driver knows how to handle resets. For all other interfaces,
+ * if the driver doesn't have pre_reset and post_reset methods then
+ * we attempt to unbind it and rebind afterward.
*/
int usb_reset_device(struct usb_device *udev)
{
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
struct usb_interface *cintf = config->interface[i];
struct usb_driver *drv;
+ int unbind = 0;
if (cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
- if (drv->pre_reset)
- (drv->pre_reset)(cintf);
- /* FIXME: Unbind if pre_reset returns an error or isn't defined */
+ if (drv->pre_reset && drv->post_reset)
+ unbind = (drv->pre_reset)(cintf);
+ else if (cintf->condition ==
+ USB_INTERFACE_BOUND)
+ unbind = 1;
+ if (unbind)
+ usb_forced_unbind_intf(cintf);
}
}
}
for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
struct usb_interface *cintf = config->interface[i];
struct usb_driver *drv;
+ int rebind = cintf->needs_binding;
- if (cintf->dev.driver) {
+ if (!rebind && cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
if (drv->post_reset)
- (drv->post_reset)(cintf);
- /* FIXME: Unbind if post_reset returns an error or isn't defined */
+ rebind = (drv->post_reset)(cintf);
+ else if (cintf->condition ==
+ USB_INTERFACE_BOUND)
+ rebind = 1;
}
+ if (rebind)
+ usb_rebind_intf(cintf);
}
}