usbcore: track whether interfaces are suspended
[deliverable/linux.git] / drivers / usb / core / hub.c
index 90b8d43c6b339eb233045dc8d550176ea67192f6..a39112041e6932131fc12db5e6042eb4527c2f66 100644 (file)
@@ -8,7 +8,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/module.h>
@@ -432,15 +431,22 @@ static void hub_power_on(struct usb_hub *hub)
 {
        int port1;
        unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
-       u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
-
-       /* if hub supports power switching, enable power on each port */
-       if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
+       u16 wHubCharacteristics =
+                       le16_to_cpu(hub->descriptor->wHubCharacteristics);
+
+       /* Enable power on each port.  Some hubs have reserved values
+        * of LPSM (> 2) in their descriptors, even though they are
+        * USB 2.0 hubs.  Some hubs do not implement port-power switching
+        * but only emulate it.  In all cases, the ports won't work
+        * unless we send these messages to the hub.
+        */
+       if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)
                dev_dbg(hub->intfdev, "enabling power on all ports\n");
-               for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
-                       set_port_feature(hub->hdev, port1,
-                                       USB_PORT_FEAT_POWER);
-       }
+       else
+               dev_dbg(hub->intfdev, "trying to enable port power on "
+                               "non-switchable hub\n");
+       for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
+               set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
 
        /* Wait at least 100 msec for power to become stable */
        msleep(max(pgood_delay, (unsigned) 100));
@@ -518,15 +524,16 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 
 
 /* caller has locked the hub device */
-static void hub_pre_reset(struct usb_hub *hub, int disable_ports)
+static void hub_pre_reset(struct usb_interface *intf)
 {
+       struct usb_hub *hub = usb_get_intfdata(intf);
        struct usb_device *hdev = hub->hdev;
        int port1;
 
        for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
                if (hdev->children[port1 - 1]) {
                        usb_disconnect(&hdev->children[port1 - 1]);
-                       if (disable_ports)
+                       if (hub->error == 0)
                                hub_port_disable(hub, port1, 0);
                }
        }
@@ -534,8 +541,10 @@ static void hub_pre_reset(struct usb_hub *hub, int disable_ports)
 }
 
 /* caller has locked the hub device */
-static void hub_post_reset(struct usb_hub *hub)
+static void hub_post_reset(struct usb_interface *intf)
 {
+       struct usb_hub *hub = usb_get_intfdata(intf);
+
        hub_activate(hub);
        hub_power_on(hub);
 }
@@ -795,15 +804,16 @@ static void hub_disconnect(struct usb_interface *intf)
        struct usb_hub *hub = usb_get_intfdata (intf);
        struct usb_device *hdev;
 
+       /* Disconnect all children and quiesce the hub */
+       hub->error = 0;
+       hub_pre_reset(intf);
+
        usb_set_intfdata (intf, NULL);
        hdev = hub->hdev;
 
        if (hdev->speed == USB_SPEED_HIGH)
                highspeed_hubs--;
 
-       /* Disconnect all children and quiesce the hub */
-       hub_pre_reset(hub, 1);
-
        usb_free_urb(hub->urb);
        hub->urb = NULL;
 
@@ -1138,136 +1148,28 @@ void usb_disconnect(struct usb_device **pdev)
         * cleaning up all state associated with the current configuration
         * so that the hardware is now fully quiesced.
         */
+       dev_dbg (&udev->dev, "unregistering device\n");
        usb_disable_device(udev, 0);
 
-       usb_notify_remove_device(udev);
+       usb_unlock_device(udev);
 
-       /* Free the device number, remove the /proc/bus/usb entry and
-        * the sysfs attributes, and delete the parent's children[]
+       /* Unregister the device.  The device driver is responsible
+        * for removing the device files from usbfs and sysfs and for
+        * de-configuring the device.
+        */
+       device_del(&udev->dev);
+
+       /* Free the device number and delete the parent's children[]
         * (or root_hub) pointer.
         */
-       dev_dbg (&udev->dev, "unregistering device\n");
        release_address(udev);
-       usb_remove_sysfs_dev_files(udev);
 
        /* Avoid races with recursively_mark_NOTATTACHED() */
        spin_lock_irq(&device_state_lock);
        *pdev = NULL;
        spin_unlock_irq(&device_state_lock);
 
-       usb_unlock_device(udev);
-
-       device_unregister(&udev->dev);
-}
-
-static inline const char *plural(int n)
-{
-       return (n == 1 ? "" : "s");
-}
-
-static int choose_configuration(struct usb_device *udev)
-{
-       int i;
-       int num_configs;
-       struct usb_host_config *c, *best;
-
-       best = NULL;
-       c = udev->config;
-       num_configs = udev->descriptor.bNumConfigurations;
-       for (i = 0; i < num_configs; (i++, c++)) {
-               struct usb_interface_descriptor *desc = NULL;
-
-               /* It's possible that a config has no interfaces! */
-               if (c->desc.bNumInterfaces > 0)
-                       desc = &c->intf_cache[0]->altsetting->desc;
-
-               /*
-                * HP's USB bus-powered keyboard has only one configuration
-                * and it claims to be self-powered; other devices may have
-                * similar errors in their descriptors.  If the next test
-                * were allowed to execute, such configurations would always
-                * be rejected and the devices would not work as expected.
-                * In the meantime, we run the risk of selecting a config
-                * that requires external power at a time when that power
-                * isn't available.  It seems to be the lesser of two evils.
-                *
-                * Bugzilla #6448 reports a device that appears to crash
-                * when it receives a GET_DEVICE_STATUS request!  We don't
-                * have any other way to tell whether a device is self-powered,
-                * but since we don't use that information anywhere but here,
-                * the call has been removed.
-                *
-                * Maybe the GET_DEVICE_STATUS call and the test below can
-                * be reinstated when device firmwares become more reliable.
-                * Don't hold your breath.
-                */
-#if 0
-               /* Rule out self-powered configs for a bus-powered device */
-               if (bus_powered && (c->desc.bmAttributes &
-                                       USB_CONFIG_ATT_SELFPOWER))
-                       continue;
-#endif
-
-               /*
-                * The next test may not be as effective as it should be.
-                * Some hubs have errors in their descriptor, claiming
-                * to be self-powered when they are really bus-powered.
-                * We will overestimate the amount of current such hubs
-                * make available for each port.
-                *
-                * This is a fairly benign sort of failure.  It won't
-                * cause us to reject configurations that we should have
-                * accepted.
-                */
-
-               /* Rule out configs that draw too much bus current */
-               if (c->desc.bMaxPower * 2 > udev->bus_mA)
-                       continue;
-
-               /* If the first config's first interface is COMM/2/0xff
-                * (MSFT RNDIS), rule it out unless Linux has host-side
-                * RNDIS support. */
-               if (i == 0 && desc
-                               && desc->bInterfaceClass == USB_CLASS_COMM
-                               && desc->bInterfaceSubClass == 2
-                               && desc->bInterfaceProtocol == 0xff) {
-#ifndef CONFIG_USB_NET_RNDIS
-                       continue;
-#else
-                       best = c;
-#endif
-               }
-
-               /* From the remaining configs, choose the first one whose
-                * first interface is for a non-vendor-specific class.
-                * Reason: Linux is more likely to have a class driver
-                * than a vendor-specific driver. */
-               else if (udev->descriptor.bDeviceClass !=
-                                               USB_CLASS_VENDOR_SPEC &&
-                               (!desc || desc->bInterfaceClass !=
-                                               USB_CLASS_VENDOR_SPEC)) {
-                       best = c;
-                       break;
-               }
-
-               /* If all the remaining configs are vendor-specific,
-                * choose the first one. */
-               else if (!best)
-                       best = c;
-       }
-
-       if (best) {
-               i = best->desc.bConfigurationValue;
-               dev_info(&udev->dev,
-                       "configuration #%d chosen from %d choice%s\n",
-                       i, num_configs, plural(num_configs));
-       } else {
-               i = -1;
-               dev_warn(&udev->dev,
-                       "no configuration chosen from %d choice%s\n",
-                       num_configs, plural(num_configs));
-       }
-       return i;
+       put_device(&udev->dev);
 }
 
 #ifdef DEBUG
@@ -1310,7 +1212,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
 int usb_new_device(struct usb_device *udev)
 {
        int err;
-       int c;
 
        err = usb_get_configuration(udev);
        if (err < 0) {
@@ -1389,9 +1290,9 @@ int usb_new_device(struct usb_device *udev)
                 * (Includes HNP test device.)
                 */
                if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
-                       static int __usb_suspend_device(struct usb_device *,
+                       static int __usb_port_suspend(struct usb_device *,
                                                int port1);
-                       err = __usb_suspend_device(udev, udev->bus->otg_port);
+                       err = __usb_port_suspend(udev, udev->bus->otg_port);
                        if (err < 0)
                                dev_dbg(&udev->dev, "HNP fail, %d\n", err);
                }
@@ -1400,34 +1301,15 @@ int usb_new_device(struct usb_device *udev)
        }
 #endif
 
-       /* put device-specific files into sysfs */
+       /* Register the device.  The device driver is responsible
+        * for adding the device files to usbfs and sysfs and for
+        * configuring the device.
+        */
        err = device_add (&udev->dev);
        if (err) {
                dev_err(&udev->dev, "can't device_add, error %d\n", err);
                goto fail;
        }
-       usb_create_sysfs_dev_files (udev);
-
-       usb_lock_device(udev);
-
-       /* choose and set the configuration. that registers the interfaces
-        * with the driver core, and lets usb device drivers bind to them.
-        */
-       c = choose_configuration(udev);
-       if (c >= 0) {
-               err = usb_set_configuration(udev, c);
-               if (err) {
-                       dev_err(&udev->dev, "can't set config #%d, error %d\n",
-                                       c, err);
-                       /* This need not be fatal.  The user can try to
-                        * set other configurations. */
-               }
-       }
-
-       /* USB device state == configured ... usable */
-       usb_notify_add_device(udev);
-
-       usb_unlock_device(udev);
 
        return 0;
 
@@ -1666,7 +1548,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
  * the root hub for their bus goes into global suspend ... so we don't
  * (falsely) update the device power state to say it suspended.
  */
-static int __usb_suspend_device (struct usb_device *udev, int port1)
+static int __usb_port_suspend (struct usb_device *udev, int port1)
 {
        int     status = 0;
 
@@ -1694,8 +1576,8 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
                }
        }
 
-       /* we only change a device's upstream USB link.
-        * root hubs have no upstream USB link.
+       /* we change the device's upstream USB link,
+        * but root hubs have no upstream USB link.
         */
        if (udev->parent)
                status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
@@ -1709,14 +1591,14 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
 #endif
 
 /*
- * usb_suspend_device - suspend a usb device
+ * usb_port_suspend - suspend a usb device's upstream port
  * @udev: device that's no longer in active use
  * Context: must be able to sleep; device not locked; pm locks held
  *
  * Suspends a USB device that isn't in active use, conserving power.
  * Devices may wake out of a suspend, if anything important happens,
  * using the remote wakeup mechanism.  They may also be taken out of
- * suspend by the host, using usb_resume_device().  It's also routine
+ * suspend by the host, using usb_port_resume().  It's also routine
  * to disconnect devices while they are suspended.
  *
  * This only affects the USB hardware for a device; its interfaces
@@ -1728,12 +1610,12 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
  *
  * Returns 0 on success, else negative errno.
  */
-int usb_suspend_device(struct usb_device *udev)
+int usb_port_suspend(struct usb_device *udev)
 {
 #ifdef CONFIG_USB_SUSPEND
        if (udev->state == USB_STATE_NOTATTACHED)
                return -ENODEV;
-       return __usb_suspend_device(udev, udev->portnum);
+       return __usb_port_suspend(udev, udev->portnum);
 #else
        /* NOTE:  udev->state unchanged, it's not lying ... */
        udev->dev.power.power_state = PMSG_SUSPEND;
@@ -1749,7 +1631,7 @@ int usb_suspend_device(struct usb_device *udev)
  * resume (by host) or remote wakeup (by device) ... now see what changed
  * in the tree that's rooted at this device.
  */
-static int finish_device_resume(struct usb_device *udev)
+static int finish_port_resume(struct usb_device *udev)
 {
        int     status;
        u16     devstatus;
@@ -1772,14 +1654,14 @@ static int finish_device_resume(struct usb_device *udev)
         * and device drivers will know about any resume quirks.
         */
        status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
-       if (status < 2)
+       if (status >= 0)
+               status = (status == 2 ? 0 : -ENODEV);
+
+       if (status)
                dev_dbg(&udev->dev,
                        "gone after usb resume? status %d\n",
                        status);
        else if (udev->actconfig) {
-               unsigned        i;
-               int             (*resume)(struct device *);
-
                le16_to_cpus(&devstatus);
                if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
                                && udev->parent) {
@@ -1790,24 +1672,9 @@ static int finish_device_resume(struct usb_device *udev)
                                        USB_DEVICE_REMOTE_WAKEUP, 0,
                                        NULL, 0,
                                        USB_CTRL_SET_TIMEOUT);
-                       if (status) {
+                       if (status)
                                dev_dbg(&udev->dev, "disable remote "
                                        "wakeup, status %d\n", status);
-                               status = 0;
-                       }
-               }
-
-               /* resume interface drivers; if this is a hub, it
-                * may have a child resume event to deal with soon
-                */
-               resume = udev->dev.bus->resume;
-               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
-                       struct device *dev =
-                                       &udev->actconfig->interface[i]->dev;
-
-                       down(&dev->sem);
-                       (void) resume(dev);
-                       up(&dev->sem);
                }
                status = 0;
 
@@ -1861,11 +1728,16 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
                        dev_dbg(hub->intfdev,
                                "port %d status %04x.%04x after resume, %d\n",
                                port1, portchange, devstatus, status);
+                       if (status >= 0)
+                               status = -ENODEV;
                } else {
+                       if (portchange & USB_PORT_STAT_C_SUSPEND)
+                               clear_port_feature(hub->hdev, port1,
+                                               USB_PORT_FEAT_C_SUSPEND);
                        /* TRSMRCY = 10 msec */
                        msleep(10);
                        if (udev)
-                               status = finish_device_resume(udev);
+                               status = finish_port_resume(udev);
                }
        }
        if (status < 0)
@@ -1877,7 +1749,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 #endif
 
 /*
- * usb_resume_device - re-activate a suspended usb device
+ * usb_port_resume - re-activate a suspended usb device's upstream port
  * @udev: device to re-activate
  * Context: must be able to sleep; device not locked; pm locks held
  *
@@ -1889,14 +1761,16 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
  *
  * Returns 0 on success, else negative errno.
  */
-int usb_resume_device(struct usb_device *udev)
+int usb_port_resume(struct usb_device *udev)
 {
        int     status;
 
        if (udev->state == USB_STATE_NOTATTACHED)
                return -ENODEV;
 
-       /* selective resume of one downstream hub-to-device port */
+       /* we change the device's upstream USB link,
+        * but root hubs have no upstream USB link.
+        */
        if (udev->parent) {
 #ifdef CONFIG_USB_SUSPEND
                if (udev->state == USB_STATE_SUSPENDED) {
@@ -1908,17 +1782,9 @@ int usb_resume_device(struct usb_device *udev)
 #endif
                        status = 0;
        } else
-               status = finish_device_resume(udev);
+               status = finish_port_resume(udev);
        if (status < 0)
-               dev_dbg(&udev->dev, "can't resume, status %d\n",
-                       status);
-
-       /* rebind drivers that had no suspend() */
-       if (status == 0) {
-               usb_unlock_device(udev);
-               bus_rescan_devices(&usb_bus_type);
-               usb_lock_device(udev);
-       }
+               dev_dbg(&udev->dev, "can't resume, status %d\n", status);
        return status;
 }
 
@@ -1936,8 +1802,11 @@ static int remote_wakeup(struct usb_device *udev)
                dev_dbg(&udev->dev, "RESUME (wakeup)\n");
                /* TRSMRCY = 10 msec */
                msleep(10);
-               status = finish_device_resume(udev);
+               status = finish_port_resume(udev);
        }
+
+       if (status == 0)
+               usb_resume_both(udev);
        usb_unlock_device(udev);
 #endif
        return status;
@@ -2009,51 +1878,8 @@ static int hub_resume(struct usb_interface *intf)
                }
        }
 
+       /* tell khubd to look for changes on this hub */
        hub_activate(hub);
-
-       /* REVISIT:  this recursion probably shouldn't exist.  Remove
-        * this code sometime, after retesting with different root and
-        * external hubs.
-        */
-#ifdef CONFIG_USB_SUSPEND
-       {
-       unsigned                port1;
-
-       for (port1 = 1; port1 <= hdev->maxchild; port1++) {
-               struct usb_device       *udev;
-               u16                     portstat, portchange;
-
-               udev = hdev->children [port1-1];
-               status = hub_port_status(hub, port1, &portstat, &portchange);
-               if (status == 0) {
-                       if (portchange & USB_PORT_STAT_C_SUSPEND) {
-                               clear_port_feature(hdev, port1,
-                                       USB_PORT_FEAT_C_SUSPEND);
-                               portchange &= ~USB_PORT_STAT_C_SUSPEND;
-                       }
-
-                       /* let khubd handle disconnects etc */
-                       if (portchange)
-                               continue;
-               }
-
-               if (!udev || status < 0)
-                       continue;
-               usb_lock_device(udev);
-               if (portstat & USB_PORT_STAT_SUSPEND)
-                       status = hub_port_resume(hub, port1, udev);
-               else {
-                       status = finish_device_resume(udev);
-                       if (status < 0) {
-                               dev_dbg(&intf->dev, "resume port %d --> %d\n",
-                                       port1, status);
-                               hub_port_logical_disconnect(hub, port1);
-                       }
-               }
-               usb_unlock_device(udev);
-       }
-       }
-#endif
        return 0;
 }
 
@@ -2710,17 +2536,6 @@ static void hub_events(void)
                usb_get_intf(intf);
                spin_unlock_irq(&hub_event_lock);
 
-               /* Is this is a root hub wanting to reactivate the downstream
-                * ports?  If so, be sure the interface resumes even if its
-                * stub "device" node was never suspended.
-                */
-               if (i) {
-                       dpm_runtime_resume(&hdev->dev);
-                       dpm_runtime_resume(&intf->dev);
-                       usb_put_intf(intf);
-                       continue;
-               }
-
                /* Lock the device, then check to see if we were
                 * disconnected while waiting for the lock to succeed. */
                if (locktree(hdev) < 0) {
@@ -2732,10 +2547,18 @@ static void hub_events(void)
 
                /* If the hub has died, clean up after it */
                if (hdev->state == USB_STATE_NOTATTACHED) {
-                       hub_pre_reset(hub, 0);
+                       hub->error = -ENODEV;
+                       hub_pre_reset(intf);
                        goto loop;
                }
 
+               /* Is this is a root hub wanting to reactivate the downstream
+                * ports?  If so, be sure the interface resumes even if its
+                * stub "device" node was never suspended.
+                */
+               if (i)
+                       usb_resume_both(hdev);
+
                /* If this is an inactive or suspended hub, do nothing */
                if (hub->quiescing)
                        goto loop;
@@ -2744,7 +2567,7 @@ static void hub_events(void)
                        dev_dbg (hub_dev, "resetting for error %d\n",
                                hub->error);
 
-                       ret = usb_reset_device(hdev);
+                       ret = usb_reset_composite_device(hdev, intf);
                        if (ret) {
                                dev_dbg (hub_dev,
                                        "error resetting hub: %d\n", ret);
@@ -2913,6 +2736,8 @@ static struct usb_driver hub_driver = {
        .disconnect =   hub_disconnect,
        .suspend =      hub_suspend,
        .resume =       hub_resume,
+       .pre_reset =    hub_pre_reset,
+       .post_reset =   hub_post_reset,
        .ioctl =        hub_ioctl,
        .id_table =     hub_id_table,
 };
@@ -2992,9 +2817,9 @@ static int config_descriptors_changed(struct usb_device *udev)
  * usb_reset_device - perform a USB port reset to reinitialize a device
  * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
  *
- * WARNING - don't reset any device unless drivers for all of its
- * interfaces are expecting that reset!  Maybe some driver->reset()
- * method should eventually help ensure sufficient cooperation.
+ * WARNING - don't use this routine to reset a composite device
+ * (one with multiple interfaces owned by separate drivers)!
+ * Use usb_reset_composite_device() instead.
  *
  * Do a port reset, reassign the device's address, and establish its
  * former operating configuration.  If the reset fails, or the device's
@@ -3018,7 +2843,6 @@ int usb_reset_device(struct usb_device *udev)
        struct usb_device               *parent_hdev = udev->parent;
        struct usb_hub                  *parent_hub;
        struct usb_device_descriptor    descriptor = udev->descriptor;
-       struct usb_hub                  *hub = NULL;
        int                             i, ret = 0;
        int                             port1 = udev->portnum;
 
@@ -3036,14 +2860,6 @@ int usb_reset_device(struct usb_device *udev)
        }
        parent_hub = hdev_to_hub(parent_hdev);
 
-       /* If we're resetting an active hub, take some special actions */
-       if (udev->actconfig && udev->actconfig->desc.bNumInterfaces > 0 &&
-                       udev->actconfig->interface[0]->dev.driver ==
-                               &hub_driver.driver &&
-                       (hub = hdev_to_hub(udev)) != NULL) {
-               hub_pre_reset(hub, 0);
-       }
-
        set_bit(port1, parent_hub->busy_bits);
        for (i = 0; i < SET_CONFIG_TRIES; ++i) {
 
@@ -3102,11 +2918,89 @@ int usb_reset_device(struct usb_device *udev)
        }
 
 done:
-       if (hub)
-               hub_post_reset(hub);
        return 0;
  
 re_enumerate:
        hub_port_logical_disconnect(parent_hub, port1);
        return -ENODEV;
 }
+EXPORT_SYMBOL(usb_reset_device);
+
+/**
+ * usb_reset_composite_device - warn interface drivers and perform a USB port reset
+ * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
+ * @iface: interface bound to the driver making the request (optional)
+ *
+ * Warns all drivers bound to registered interfaces (using their pre_reset
+ * method), performs the port reset, and then lets the drivers know that
+ * the reset is over (using their post_reset method).
+ *
+ * Return value is the same as for usb_reset_device().
+ *
+ * The caller must own the device lock.  For example, it's safe to use
+ * 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().
+ *
+ * The interface locks are acquired during the pre_reset stage and released
+ * during the post_reset stage.  However if iface is not NULL and is
+ * currently being probed, we assume that the caller already owns its
+ * lock.
+ */
+int usb_reset_composite_device(struct usb_device *udev,
+               struct usb_interface *iface)
+{
+       int ret;
+       struct usb_host_config *config = udev->actconfig;
+
+       if (udev->state == USB_STATE_NOTATTACHED ||
+                       udev->state == USB_STATE_SUSPENDED) {
+               dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
+                               udev->state);
+               return -EINVAL;
+       }
+
+       if (iface && iface->condition != USB_INTERFACE_BINDING)
+               iface = NULL;
+
+       if (config) {
+               int i;
+               struct usb_interface *cintf;
+               struct usb_driver *drv;
+
+               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+                       cintf = config->interface[i];
+                       if (cintf != iface)
+                               down(&cintf->dev.sem);
+                       if (device_is_registered(&cintf->dev) &&
+                                       cintf->dev.driver) {
+                               drv = to_usb_driver(cintf->dev.driver);
+                               if (drv->pre_reset)
+                                       (drv->pre_reset)(cintf);
+                       }
+               }
+       }
+
+       ret = usb_reset_device(udev);
+
+       if (config) {
+               int i;
+               struct usb_interface *cintf;
+               struct usb_driver *drv;
+
+               for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
+                       cintf = config->interface[i];
+                       if (device_is_registered(&cintf->dev) &&
+                                       cintf->dev.driver) {
+                               drv = to_usb_driver(cintf->dev.driver);
+                               if (drv->post_reset)
+                                       (drv->post_reset)(cintf);
+                       }
+                       if (cintf != iface)
+                               up(&cintf->dev.sem);
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(usb_reset_composite_device);
This page took 0.034016 seconds and 5 git commands to generate.