]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/core/hub.c
USB Core: hub.c: prevent re-enumeration on HNP
[linux-2.6-omap-h63xx.git] / drivers / usb / core / hub.c
index bde29ab2b5042192115a220b8eeda0982749b2f3..7946d7b6c71a03997e81667e08a9f4cb242d8d89 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/sched.h>
 #include <linux/list.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/ioctl.h>
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
@@ -35,6 +34,7 @@
 struct usb_hub {
        struct device           *intfdev;       /* the "interface" device */
        struct usb_device       *hdev;
+       struct kref             kref;
        struct urb              *urb;           /* for interrupt polling pipe */
 
        /* buffer for urb ... with extra space in case of babble */
@@ -67,6 +67,7 @@ struct usb_hub {
        unsigned                limited_power:1;
        unsigned                quiescing:1;
        unsigned                activating:1;
+       unsigned                disconnected:1;
 
        unsigned                has_indicators:1;
        u8                      indicator[USB_MAXCHILDREN];
@@ -322,7 +323,7 @@ static void kick_khubd(struct usb_hub *hub)
        to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
 
        spin_lock_irqsave(&hub_event_lock, flags);
-       if (list_empty(&hub->event_list)) {
+       if (!hub->disconnected & list_empty(&hub->event_list)) {
                list_add_tail(&hub->event_list, &hub_event_list);
                wake_up(&khubd_wait);
        }
@@ -331,6 +332,7 @@ static void kick_khubd(struct usb_hub *hub)
 
 void usb_kick_khubd(struct usb_device *hdev)
 {
+       /* FIXME: What if hdev isn't bound to the hub driver? */
        kick_khubd(hdev_to_hub(hdev));
 }
 
@@ -401,9 +403,10 @@ static void hub_tt_kevent (struct work_struct *work)
        struct usb_hub          *hub =
                container_of(work, struct usb_hub, tt.kevent);
        unsigned long           flags;
+       int                     limit = 100;
 
        spin_lock_irqsave (&hub->tt.lock, flags);
-       while (!list_empty (&hub->tt.clear_list)) {
+       while (--limit && !list_empty (&hub->tt.clear_list)) {
                struct list_head        *temp;
                struct usb_tt_clear     *clear;
                struct usb_device       *hdev = hub->hdev;
@@ -551,45 +554,121 @@ static int hub_hub_status(struct usb_hub *hub,
 static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 {
        struct usb_device *hdev = hub->hdev;
-       int ret;
+       int ret = 0;
 
-       if (hdev->children[port1-1] && set_state) {
+       if (hdev->children[port1-1] && set_state)
                usb_set_device_state(hdev->children[port1-1],
                                USB_STATE_NOTATTACHED);
-       }
-       ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+       if (!hub->error)
+               ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
        if (ret)
                dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
-                       port1, ret);
-
+                               port1, ret);
        return ret;
 }
 
+/*
+ * Disable a port and mark a logical connnect-change event, so that some
+ * time later khubd will disconnect() any existing usb_device on the port
+ * and will re-enumerate if there actually is a device attached.
+ */
+static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
+{
+       dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
+       hub_port_disable(hub, port1, 1);
 
-/* caller has locked the hub device */
-static void hub_pre_reset(struct usb_interface *intf)
+       /* FIXME let caller ask to power down the port:
+        *  - some devices won't enumerate without a VBUS power cycle
+        *  - SRP saves power that way
+        *  - ... new call, TBD ...
+        * That's easy if this hub can switch power per-port, and
+        * khubd reactivates the port later (timer, SRP, etc).
+        * Powerdown must be optional, because of reset/DFU.
+        */
+
+       set_bit(port1, hub->change_bits);
+       kick_khubd(hub);
+}
+
+static void disconnect_all_children(struct usb_hub *hub, int logical)
 {
-       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 (hub->error == 0)
-                               hub_port_disable(hub, port1, 0);
+               if (hdev->children[port1-1]) {
+                       if (logical)
+                               hub_port_logical_disconnect(hub, port1);
+                       else
+                               usb_disconnect(&hdev->children[port1-1]);
                }
        }
+}
+
+#ifdef CONFIG_USB_PERSIST
+
+#define USB_PERSIST    1
+
+/* For "persistent-device" resets we must mark the child devices for reset
+ * and turn off a possible connect-change status (so khubd won't disconnect
+ * them later).
+ */
+static void mark_children_for_reset_resume(struct usb_hub *hub)
+{
+       struct usb_device *hdev = hub->hdev;
+       int port1;
+
+       for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+               struct usb_device *child = hdev->children[port1-1];
+
+               if (child) {
+                       child->reset_resume = 1;
+                       clear_port_feature(hdev, port1,
+                                       USB_PORT_FEAT_C_CONNECTION);
+               }
+       }
+}
+
+#else
+
+#define USB_PERSIST    0
+
+static inline void mark_children_for_reset_resume(struct usb_hub *hub)
+{ }
+
+#endif /* CONFIG_USB_PERSIST */
+
+/* caller has locked the hub device */
+static void hub_pre_reset(struct usb_interface *intf)
+{
+       struct usb_hub *hub = usb_get_intfdata(intf);
+
+       /* This routine doesn't run as part of a reset-resume, so it's safe
+        * to disconnect all the drivers below the hub.
+        */
+       disconnect_all_children(hub, 0);
        hub_quiesce(hub);
 }
 
 /* caller has locked the hub device */
-static void hub_post_reset(struct usb_interface *intf)
+static void hub_post_reset(struct usb_interface *intf, int reset_resume)
 {
        struct usb_hub *hub = usb_get_intfdata(intf);
 
-       hub_activate(hub);
        hub_power_on(hub);
+       if (reset_resume) {
+               if (USB_PERSIST)
+                       mark_children_for_reset_resume(hub);
+               else {
+                       /* Reset-resume doesn't call pre_reset, so we have to
+                        * disconnect the children here.  But we may not lock
+                        * the child devices, so we have to do a "logical"
+                        * disconnect.
+                        */
+                       disconnect_all_children(hub, 1);
+               }
+       }
+       hub_activate(hub);
 }
 
 
@@ -846,43 +925,42 @@ fail:
        return ret;
 }
 
+static void hub_release(struct kref *kref)
+{
+       struct usb_hub *hub = container_of(kref, struct usb_hub, kref);
+
+       usb_put_intf(to_usb_interface(hub->intfdev));
+       kfree(hub);
+}
+
 static unsigned highspeed_hubs;
 
 static void hub_disconnect(struct usb_interface *intf)
 {
        struct usb_hub *hub = usb_get_intfdata (intf);
-       struct usb_device *hdev;
+
+       /* Take the hub off the event list and don't let it be added again */
+       spin_lock_irq(&hub_event_lock);
+       list_del_init(&hub->event_list);
+       hub->disconnected = 1;
+       spin_unlock_irq(&hub_event_lock);
 
        /* 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)
+       if (hub->hdev->speed == USB_SPEED_HIGH)
                highspeed_hubs--;
 
        usb_free_urb(hub->urb);
-       hub->urb = NULL;
-
-       spin_lock_irq(&hub_event_lock);
-       list_del_init(&hub->event_list);
-       spin_unlock_irq(&hub_event_lock);
-
        kfree(hub->descriptor);
-       hub->descriptor = NULL;
-
        kfree(hub->status);
-       hub->status = NULL;
+       usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
+                       hub->buffer_dma);
 
-       if (hub->buffer) {
-               usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer,
-                               hub->buffer_dma);
-               hub->buffer = NULL;
-       }
-
-       kfree(hub);
+       kref_put(&hub->kref, hub_release);
 }
 
 static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -930,10 +1008,12 @@ descriptor_error:
                return -ENOMEM;
        }
 
+       kref_init(&hub->kref);
        INIT_LIST_HEAD(&hub->event_list);
        hub->intfdev = &intf->dev;
        hub->hdev = hdev;
        INIT_DELAYED_WORK(&hub->leds, led_work);
+       usb_get_intf(intf);
 
        usb_set_intfdata (intf, hub);
        intf->needs_remote_wakeup = 1;
@@ -983,49 +1063,6 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
 }
 
 
-/* grab device/port lock, returning index of that port (zero based).
- * protects the upstream link used by this device from concurrent
- * tree operations like suspend, resume, reset, and disconnect, which
- * apply to everything downstream of a given port.
- */
-static int locktree(struct usb_device *udev)
-{
-       int                     t;
-       struct usb_device       *hdev;
-
-       if (!udev)
-               return -ENODEV;
-
-       /* root hub is always the first lock in the series */
-       hdev = udev->parent;
-       if (!hdev) {
-               usb_lock_device(udev);
-               return 0;
-       }
-
-       /* on the path from root to us, lock everything from
-        * top down, dropping parent locks when not needed
-        */
-       t = locktree(hdev);
-       if (t < 0)
-               return t;
-
-       /* everything is fail-fast once disconnect
-        * processing starts
-        */
-       if (udev->state == USB_STATE_NOTATTACHED) {
-               usb_unlock_device(hdev);
-               return -ENODEV;
-       }
-
-       /* when everyone grabs locks top->bottom,
-        * non-overlapping work may be concurrent
-        */
-       usb_lock_device(udev);
-       usb_unlock_device(hdev);
-       return udev->portnum;
-}
-
 static void recursively_mark_NOTATTACHED(struct usb_device *udev)
 {
        int i;
@@ -1093,38 +1130,64 @@ void usb_set_device_state(struct usb_device *udev,
 
 #ifdef CONFIG_PM
 
+/**
+ * usb_reset_suspended_device - reset a suspended device instead of resuming it
+ * @udev: device to be reset instead of resumed
+ *
+ * If a host controller doesn't maintain VBUS suspend current during a
+ * system sleep or is reset when the system wakes up, all the USB
+ * power sessions below it will be broken.  This is especially troublesome
+ * for mass-storage devices containing mounted filesystems, since the
+ * device will appear to have disconnected and all the memory mappings
+ * to it will be lost.
+ *
+ * As an alternative, this routine attempts to recover power sessions for
+ * devices that are still present by resetting them instead of resuming
+ * them.  If all goes well, the devices will appear to persist across the
+ * the interruption of the power sessions.
+ *
+ * This facility is inherently dangerous.  Although usb_reset_device()
+ * makes every effort to insure that the same device is present after the
+ * reset as before, it cannot provide a 100% guarantee.  Furthermore it's
+ * quite possible for a device to remain unaltered but its media to be
+ * changed.  If the user replaces a flash memory card while the system is
+ * asleep, he will have only himself to blame when the filesystem on the
+ * new card is corrupted and the system crashes.
+ */
+int usb_reset_suspended_device(struct usb_device *udev)
+{
+       int rc = 0;
+
+       dev_dbg(&udev->dev, "usb %sresume\n", "reset-");
+
+       /* After we're done the device won't be suspended any more.
+        * In addition, the reset won't work if udev->state is SUSPENDED.
+        */
+       usb_set_device_state(udev, udev->actconfig
+                       ? USB_STATE_CONFIGURED
+                       : USB_STATE_ADDRESS);
+
+       /* Root hubs don't need to be (and can't be) reset */
+       if (udev->parent)
+               rc = usb_reset_device(udev);
+       return rc;
+}
+
 /**
  * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
  * @rhdev: struct usb_device for the root hub
  *
  * The USB host controller driver calls this function when its root hub
  * is resumed and Vbus power has been interrupted or the controller
- * has been reset.  The routine marks all the children of the root hub
- * as NOTATTACHED and marks logical connect-change events on their ports.
+ * has been reset.  The routine marks @rhdev as having lost power.  When
+ * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST
+ * is enabled then it will carry out power-session recovery, otherwise
+ * it will disconnect all the child devices.
  */
 void usb_root_hub_lost_power(struct usb_device *rhdev)
 {
-       struct usb_hub *hub;
-       int port1;
-       unsigned long flags;
-
        dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
-
-       /* Make sure no potential wakeup events get lost,
-        * by forcing the root hub to be resumed.
-        */
-       rhdev->dev.power.prev_state.event = PM_EVENT_ON;
-
-       spin_lock_irqsave(&device_state_lock, flags);
-       hub = hdev_to_hub(rhdev);
-       for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
-               if (rhdev->children[port1 - 1]) {
-                       recursively_mark_NOTATTACHED(
-                                       rhdev->children[port1 - 1]);
-                       set_bit(port1, hub->change_bits);
-               }
-       }
-       spin_unlock_irqrestore(&device_state_lock, flags);
+       rhdev->reset_resume = 1;
 }
 EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
 
@@ -1159,6 +1222,30 @@ static void release_address(struct usb_device *udev)
        }
 }
 
+#ifdef CONFIG_USB_SUSPEND
+
+static void usb_stop_pm(struct usb_device *udev)
+{
+       /* Synchronize with the ksuspend thread to prevent any more
+        * autosuspend requests from being submitted, and decrement
+        * the parent's count of unsuspended children.
+        */
+       usb_pm_lock(udev);
+       if (udev->parent && !udev->discon_suspended)
+               usb_autosuspend_device(udev->parent);
+       usb_pm_unlock(udev);
+
+       /* Stop any autosuspend requests already submitted */
+       cancel_rearming_delayed_work(&udev->autosuspend);
+}
+
+#else
+
+static inline void usb_stop_pm(struct usb_device *udev)
+{ }
+
+#endif
+
 /**
  * usb_disconnect - disconnect a device (usbcore-internal)
  * @pdev: pointer to device being disconnected
@@ -1225,13 +1312,7 @@ void usb_disconnect(struct usb_device **pdev)
        *pdev = NULL;
        spin_unlock_irq(&device_state_lock);
 
-       /* Decrement the parent's count of unsuspended children */
-       if (udev->parent) {
-               usb_pm_lock(udev);
-               if (!udev->discon_suspended)
-                       usb_autosuspend_device(udev->parent);
-               usb_pm_unlock(udev);
-       }
+       usb_stop_pm(udev);
 
        put_device(&udev->dev);
 }
@@ -1362,7 +1443,7 @@ int usb_new_device(struct usb_device *udev)
                        if (err < 0)
                                dev_dbg(&udev->dev, "HNP fail, %d\n", err);
                }
-               err = -ENODEV;
+               err = -ENOTSUPP;
                goto fail;
        }
 #endif
@@ -1459,9 +1540,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                if (!(portstatus & USB_PORT_STAT_CONNECTION))
                        return -ENOTCONN;
 
-               /* bomb out completely if something weird happened */
+               /* bomb out completely if the connection bounced */
                if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                       return -EINVAL;
+                       return -ENOTCONN;
 
                /* if we`ve finished resetting, then break out of the loop */
                if (!(portstatus & USB_PORT_STAT_RESET) &&
@@ -1540,29 +1621,6 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
        return status;
 }
 
-/*
- * Disable a port and mark a logical connnect-change event, so that some
- * time later khubd will disconnect() any existing usb_device on the port
- * and will re-enumerate if there actually is a device attached.
- */
-static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
-{
-       dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
-       hub_port_disable(hub, port1, 1);
-
-       /* FIXME let caller ask to power down the port:
-        *  - some devices won't enumerate without a VBUS power cycle
-        *  - SRP saves power that way
-        *  - ... new call, TBD ...
-        * That's easy if this hub can switch power per-port, and
-        * khubd reactivates the port later (timer, SRP, etc).
-        * Powerdown must be optional, because of reset/DFU.
-        */
-
-       set_bit(port1, hub->change_bits);
-       kick_khubd(hub);
-}
-
 #ifdef CONFIG_PM
 
 #ifdef CONFIG_USB_SUSPEND
@@ -1904,7 +1962,6 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
        struct usb_hub          *hub = usb_get_intfdata (intf);
        struct usb_device       *hdev = hub->hdev;
        unsigned                port1;
-       int                     status = 0;
 
        /* fail if children aren't already suspended */
        for (port1 = 1; port1 <= hdev->maxchild; port1++) {
@@ -1930,44 +1987,15 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
 
        /* stop khubd and related activity */
        hub_quiesce(hub);
-
-       /* "global suspend" of the downstream HC-to-USB interface */
-       if (!hdev->parent) {
-               status = hcd_bus_suspend(hdev->bus);
-               if (status != 0) {
-                       dev_dbg(&hdev->dev, "'global' suspend %d\n", status);
-                       hub_activate(hub);
-               }
-       }
-       return status;
+       return 0;
 }
 
 static int hub_resume(struct usb_interface *intf)
 {
        struct usb_hub          *hub = usb_get_intfdata (intf);
-       struct usb_device       *hdev = hub->hdev;
-       int                     status;
 
        dev_dbg(&intf->dev, "%s\n", __FUNCTION__);
 
-       /* "global resume" of the downstream HC-to-USB interface */
-       if (!hdev->parent) {
-               struct usb_bus  *bus = hdev->bus;
-               if (bus) {
-                       status = hcd_bus_resume (bus);
-                       if (status) {
-                               dev_dbg(&intf->dev, "'global' resume %d\n",
-                                       status);
-                               return status;
-                       }
-               } else
-                       return -EOPNOTSUPP;
-               if (status == 0) {
-                       /* TRSMRCY = 10 msec */
-                       msleep(10);
-               }
-       }
-
        /* tell khubd to look for changes on this hub */
        hub_activate(hub);
        return 0;
@@ -2202,14 +2230,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                                continue;
                        }
 
-                       /* Use a short timeout the first time through,
-                        * so that recalcitrant full-speed devices with
-                        * 8- or 16-byte ep0-maxpackets won't slow things
-                        * down tremendously by NAKing the unexpectedly
-                        * early status stage.  Also, retry on all errors;
-                        * some devices are flakey.
-                        * 255 is for WUSB devices, we actually need to use 512.
-                        * WUSB1.0[4.8.1].
+                       /* Retry on all errors; some devices are flakey.
+                        * 255 is for WUSB devices, we actually need to use
+                        * 512 (WUSB1.0[4.8.1]).
                         */
                        for (j = 0; j < 3; ++j) {
                                buf->bMaxPacketSize0 = 0;
@@ -2217,7 +2240,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                                        USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                                        USB_DT_DEVICE << 8, 0,
                                        buf, GET_DESCRIPTOR_BUFSIZE,
-                                       (i ? USB_CTRL_GET_TIMEOUT : 1000));
+                                       USB_CTRL_GET_TIMEOUT);
                                switch (buf->bMaxPacketSize0) {
                                case 8: case 16: case 32: case 64: case 255:
                                        if (buf->bDescriptorType ==
@@ -2427,10 +2450,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 
        if (portchange & USB_PORT_STAT_C_CONNECTION) {
                status = hub_port_debounce(hub, port1);
-               if (status < 0 && printk_ratelimit()) {
-                       dev_err (hub_dev,
-                               "connect-debounce failed, port %d disabled\n",
-                               port1);
+               if (status < 0) {
+                       if (printk_ratelimit())
+                               dev_err (hub_dev, "connect-debounce failed, "
+                                               "port %d disabled\n", port1);
                        goto done;
                }
                portstatus = status;
@@ -2449,19 +2472,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                return;
        }
 
-#ifdef  CONFIG_USB_SUSPEND
-       /* If something is connected, but the port is suspended, wake it up. */
-       if (portstatus & USB_PORT_STAT_SUSPEND) {
-               status = hub_port_resume(hub, port1, NULL);
-               if (status < 0) {
-                       dev_dbg(hub_dev,
-                               "can't clear suspend on port %d; %d\n",
-                               port1, status);
-                       goto done;
-               }
-       }
-#endif
-
        for (i = 0; i < SET_CONFIG_TRIES; i++) {
                struct usb_device *udev;
 
@@ -2572,7 +2582,7 @@ loop:
                ep0_reinit(udev);
                release_address(udev);
                usb_put_dev(udev);
-               if (status == -ENOTCONN)
+               if ((status == -ENOTCONN) || (status == -ENOTSUPP))
                        break;
        }
  
@@ -2613,10 +2623,12 @@ static void hub_events(void)
                list_del_init(tmp);
 
                hub = list_entry(tmp, struct usb_hub, event_list);
-               hdev = hub->hdev;
-               intf = to_usb_interface(hub->intfdev);
-               hub_dev = &intf->dev;
+               kref_get(&hub->kref);
+               spin_unlock_irq(&hub_event_lock);
 
+               hdev = hub->hdev;
+               hub_dev = hub->intfdev;
+               intf = to_usb_interface(hub_dev);
                dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
                                hdev->state, hub->descriptor
                                        ? hub->descriptor->bNbrPorts
@@ -2625,16 +2637,10 @@ static void hub_events(void)
                                (u16) hub->change_bits[0],
                                (u16) hub->event_bits[0]);
 
-               usb_get_intf(intf);
-               spin_unlock_irq(&hub_event_lock);
-
                /* Lock the device, then check to see if we were
                 * disconnected while waiting for the lock to succeed. */
-               if (locktree(hdev) < 0) {
-                       usb_put_intf(intf);
-                       continue;
-               }
-               if (hub != usb_get_intfdata(intf))
+               usb_lock_device(hdev);
+               if (unlikely(hub->disconnected))
                        goto loop;
 
                /* If the hub has died, clean up after it */
@@ -2797,7 +2803,7 @@ loop_autopm:
                        usb_autopm_enable(intf);
 loop:
                usb_unlock_device(hdev);
-               usb_put_intf(intf);
+               kref_put(&hub->kref, hub_release);
 
         } /* end while (1) */
 }
@@ -2934,6 +2940,11 @@ static int config_descriptors_changed(struct usb_device *udev)
  * 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().
+ *
+ * Locking exception: This routine may also be called from within an
+ * autoresume handler.  Such usage won't conflict with other tasks
+ * holding the device lock because these tasks should always call
+ * usb_autopm_resume_device(), thereby preventing any unwanted autoresume.
  */
 int usb_reset_device(struct usb_device *udev)
 {
@@ -2964,7 +2975,7 @@ int usb_reset_device(struct usb_device *udev)
                 * Other endpoints will be handled by re-enumeration. */
                ep0_reinit(udev);
                ret = hub_port_init(parent_hub, udev, port1, i);
-               if (ret >= 0)
+               if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
                        break;
        }
        clear_bit(port1, parent_hub->busy_bits);
@@ -3097,7 +3108,7 @@ int usb_reset_composite_device(struct usb_device *udev,
                                        cintf->dev.driver) {
                                drv = to_usb_driver(cintf->dev.driver);
                                if (drv->post_reset)
-                                       (drv->post_reset)(cintf);
+                                       (drv->post_reset)(cintf, 0);
                        }
                        if (cintf != iface)
                                up(&cintf->dev.sem);