]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/usb/core/driver.c
Merge branch 'mvebu/soc-orion5x' into next/dt
[karo-tx-linux.git] / drivers / usb / core / driver.c
index ab90a01568283c3c0d2a0a3df9b653bfba65a812..888881e5f2926b75dfd86ce44d668adb0b7e6bf6 100644 (file)
@@ -312,9 +312,9 @@ static int usb_probe_interface(struct device *dev)
                return error;
        }
 
-       id = usb_match_id(intf, driver->id_table);
+       id = usb_match_dynamic_id(intf, driver);
        if (!id)
-               id = usb_match_dynamic_id(intf, driver);
+               id = usb_match_id(intf, driver->id_table);
        if (!id)
                return error;
 
@@ -400,8 +400,9 @@ static int usb_unbind_interface(struct device *dev)
 {
        struct usb_driver *driver = to_usb_driver(dev->driver);
        struct usb_interface *intf = to_usb_interface(dev);
+       struct usb_host_endpoint *ep, **eps = NULL;
        struct usb_device *udev;
-       int error, r, lpm_disable_error;
+       int i, j, error, r, lpm_disable_error;
 
        intf->condition = USB_INTERFACE_UNBINDING;
 
@@ -425,6 +426,26 @@ static int usb_unbind_interface(struct device *dev)
        driver->disconnect(intf);
        usb_cancel_queued_reset(intf);
 
+       /* Free streams */
+       for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+               ep = &intf->cur_altsetting->endpoint[i];
+               if (ep->streams == 0)
+                       continue;
+               if (j == 0) {
+                       eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *),
+                                     GFP_KERNEL);
+                       if (!eps) {
+                               dev_warn(dev, "oom, leaking streams\n");
+                               break;
+                       }
+               }
+               eps[j++] = ep;
+       }
+       if (j) {
+               usb_free_streams(intf, eps, j, GFP_KERNEL);
+               kfree(eps);
+       }
+
        /* Reset other interface state.
         * We cannot do a Set-Interface if the device is suspended or
         * if it is prepared for a system sleep (since installing a new
@@ -990,8 +1011,7 @@ EXPORT_SYMBOL_GPL(usb_deregister);
  * 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.
+ * The caller must hold @intf's device's lock, but not @intf's lock.
  */
 void usb_forced_unbind_intf(struct usb_interface *intf)
 {
@@ -1004,16 +1024,37 @@ void usb_forced_unbind_intf(struct usb_interface *intf)
        intf->needs_binding = 1;
 }
 
+/*
+ * Unbind drivers for @udev's marked interfaces.  These interfaces have
+ * the needs_binding flag set, for example by usb_resume_interface().
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_marked_interfaces(struct usb_device *udev)
+{
+       struct usb_host_config  *config;
+       int                     i;
+       struct usb_interface    *intf;
+
+       config = udev->actconfig;
+       if (config) {
+               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+                       intf = config->interface[i];
+                       if (intf->dev.driver && intf->needs_binding)
+                               usb_forced_unbind_intf(intf);
+               }
+       }
+}
+
 /* 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.
+ * The caller must hold @intf's device's lock, but not @intf's lock.
  *
  * Note: Rebinds will be skipped if a system sleep transition is in
  * progress and the PM "complete" callback hasn't occurred yet.
  */
-void usb_rebind_intf(struct usb_interface *intf)
+static void usb_rebind_intf(struct usb_interface *intf)
 {
        int rc;
 
@@ -1030,68 +1071,66 @@ void usb_rebind_intf(struct usb_interface *intf)
        }
 }
 
-#ifdef CONFIG_PM
-
-/* Unbind drivers for @udev's interfaces that don't support suspend/resume
- * There is no check for reset_resume here because it can be determined
- * only during resume whether reset_resume is needed.
+/*
+ * Rebind drivers to @udev's marked interfaces.  These interfaces have
+ * the needs_binding flag set.
  *
  * The caller must hold @udev's device lock.
  */
-static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
+static void rebind_marked_interfaces(struct usb_device *udev)
 {
        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];
-
-                       if (intf->dev.driver) {
-                               drv = to_usb_driver(intf->dev.driver);
-                               if (!drv->suspend || !drv->resume)
-                                       usb_forced_unbind_intf(intf);
-                       }
+                       if (intf->needs_binding)
+                               usb_rebind_intf(intf);
                }
        }
 }
 
-/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
- * These interfaces have the needs_binding flag set by usb_resume_interface().
+/*
+ * Unbind all of @udev's marked interfaces and then rebind all of them.
+ * This ordering is necessary because some drivers claim several interfaces
+ * when they are first probed.
  *
  * The caller must hold @udev's device lock.
  */
-static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
+void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
 {
-       struct usb_host_config  *config;
-       int                     i;
-       struct usb_interface    *intf;
-
-       config = udev->actconfig;
-       if (config) {
-               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
-                       intf = config->interface[i];
-                       if (intf->dev.driver && intf->needs_binding)
-                               usb_forced_unbind_intf(intf);
-               }
-       }
+       unbind_marked_interfaces(udev);
+       rebind_marked_interfaces(udev);
 }
 
-static void do_rebind_interfaces(struct usb_device *udev)
+#ifdef CONFIG_PM
+
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+ * There is no check for reset_resume here because it can be determined
+ * only during resume whether reset_resume is needed.
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
 {
        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];
-                       if (intf->needs_binding)
-                               usb_rebind_intf(intf);
+
+                       if (intf->dev.driver) {
+                               drv = to_usb_driver(intf->dev.driver);
+                               if (!drv->suspend || !drv->resume)
+                                       usb_forced_unbind_intf(intf);
+                       }
                }
        }
 }
@@ -1420,7 +1459,7 @@ int usb_resume_complete(struct device *dev)
         * whose needs_binding flag is set
         */
        if (udev->state != USB_STATE_NOTATTACHED)
-               do_rebind_interfaces(udev);
+               rebind_marked_interfaces(udev);
        return 0;
 }
 
@@ -1442,7 +1481,7 @@ int usb_resume(struct device *dev, pm_message_t msg)
                pm_runtime_disable(dev);
                pm_runtime_set_active(dev);
                pm_runtime_enable(dev);
-               unbind_no_reset_resume_drivers_interfaces(udev);
+               unbind_marked_interfaces(udev);
        }
 
        /* Avoid PM error messages for devices disconnected while suspended