|
@@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev)
|
|
|
|
|
|
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");
|
|
@@ -311,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|
|
|
|
|
dev->driver = &driver->drvwrap.driver;
|
|
|
usb_set_intfdata(iface, priv);
|
|
|
+ iface->needs_binding = 0;
|
|
|
|
|
|
usb_pm_lock(udev);
|
|
|
iface->condition = USB_INTERFACE_BOUND;
|
|
@@ -772,6 +774,104 @@ void usb_deregister(struct usb_driver *driver)
|
|
|
}
|
|
|
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 */
|
|
@@ -841,7 +941,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
|
|
|
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);
|
|
@@ -849,12 +949,10 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
|
|
|
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);
|
|
|
}
|
|
|
|
|
@@ -878,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
|
|
|
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) {
|
|
@@ -891,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int 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);
|
|
|
}
|
|
@@ -902,7 +1002,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
|
|
|
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);
|
|
|
}
|
|
@@ -910,11 +1010,10 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -1470,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
|
|
|
{
|
|
|
int status;
|
|
|
|
|
|
+ do_unbind_rebind(udev, DO_UNBIND);
|
|
|
usb_pm_lock(udev);
|
|
|
udev->auto_pm = 0;
|
|
|
status = usb_suspend_both(udev, msg);
|
|
@@ -1497,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev)
|
|
|
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. */
|