Merge ../linux-2.6
[powerpc.git] / drivers / usb / core / hub.c
index 8407279..650d5ee 100644 (file)
@@ -32,7 +32,7 @@
 #include "hub.h"
 
 /* Protect struct usb_device->state and ->children members
- * Note: Both are also protected by ->serialize, except that ->state can
+ * Note: Both are also protected by ->dev.sem, except that ->state can
  * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
 static DEFINE_SPINLOCK(device_state_lock);
 
@@ -515,6 +515,31 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
        return ret;
 }
 
+
+/* caller has locked the hub device */
+static void hub_pre_reset(struct usb_hub *hub, int disable_ports)
+{
+       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)
+                               hub_port_disable(hub, port1, 0);
+               }
+       }
+       hub_quiesce(hub);
+}
+
+/* caller has locked the hub device */
+static void hub_post_reset(struct usb_hub *hub)
+{
+       hub_activate(hub);
+       hub_power_on(hub);
+}
+
+
 static int hub_configure(struct usb_hub *hub,
        struct usb_endpoint_descriptor *endpoint)
 {
@@ -677,26 +702,40 @@ static int hub_configure(struct usb_hub *hub,
         * and battery-powered root hubs (may provide just 8 mA).
         */
        ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
-       if (ret < 0) {
+       if (ret < 2) {
                message = "can't get hub status";
                goto fail;
        }
        le16_to_cpus(&hubstatus);
        if (hdev == hdev->bus->root_hub) {
-               struct usb_hcd *hcd =
-                               container_of(hdev->bus, struct usb_hcd, self);
-
-               hub->power_budget = min(500u, hcd->power_budget) / 2;
+               if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
+                       hub->mA_per_port = 500;
+               else {
+                       hub->mA_per_port = hdev->bus_mA;
+                       hub->limited_power = 1;
+               }
        } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
                dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
                        hub->descriptor->bHubContrCurrent);
-               hub->power_budget = (501 - hub->descriptor->bHubContrCurrent)
-                                       / 2;
+               hub->limited_power = 1;
+               if (hdev->maxchild > 0) {
+                       int remaining = hdev->bus_mA -
+                                       hub->descriptor->bHubContrCurrent;
+
+                       if (remaining < hdev->maxchild * 100)
+                               dev_warn(hub_dev,
+                                       "insufficient power available "
+                                       "to use all downstream ports\n");
+                       hub->mA_per_port = 100;         /* 7.2.1.1 */
+               }
+       } else {        /* Self-powered external hub */
+               /* FIXME: What about battery-powered external hubs that
+                * provide less current per port? */
+               hub->mA_per_port = 500;
        }
-       if (hub->power_budget)
-               dev_dbg(hub_dev, "%dmA bus power budget for children\n",
-                       hub->power_budget * 2);
-
+       if (hub->mA_per_port < 500)
+               dev_dbg(hub_dev, "%umA bus power budget for each child\n",
+                               hub->mA_per_port);
 
        ret = hub_hub_status(hub, &hubstatus, &hubchange);
        if (ret < 0) {
@@ -750,29 +789,10 @@ fail:
 
 static unsigned highspeed_hubs;
 
-/* Called after the hub driver is unbound from a hub with children */
-static void hub_remove_children_work(void *__hub)
-{
-       struct usb_hub          *hub = __hub;
-       struct usb_device       *hdev = hub->hdev;
-       int                     i;
-
-       kfree(hub);
-
-       usb_lock_device(hdev);
-       for (i = 0; i < hdev->maxchild; ++i) {
-               if (hdev->children[i])
-                       usb_disconnect(&hdev->children[i]);
-       }
-       usb_unlock_device(hdev);
-       usb_put_dev(hdev);
-}
-
 static void hub_disconnect(struct usb_interface *intf)
 {
        struct usb_hub *hub = usb_get_intfdata (intf);
        struct usb_device *hdev;
-       int n, port1;
 
        usb_set_intfdata (intf, NULL);
        hdev = hub->hdev;
@@ -780,7 +800,9 @@ static void hub_disconnect(struct usb_interface *intf)
        if (hdev->speed == USB_SPEED_HIGH)
                highspeed_hubs--;
 
-       hub_quiesce(hub);
+       /* Disconnect all children and quiesce the hub */
+       hub_pre_reset(hub, 1);
+
        usb_free_urb(hub->urb);
        hub->urb = NULL;
 
@@ -800,27 +822,7 @@ static void hub_disconnect(struct usb_interface *intf)
                hub->buffer = NULL;
        }
 
-       /* If there are any children then this is an unbind only, not a
-        * physical disconnection.  The active ports must be disabled
-        * and later on we must call usb_disconnect().  We can't call
-        * it now because we may not hold the hub's device lock.
-        */
-       n = 0;
-       for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
-               if (hdev->children[port1 - 1]) {
-                       ++n;
-                       hub_port_disable(hub, port1, 1);
-               }
-       }
-
-       if (n == 0)
-               kfree(hub);
-       else {
-               /* Reuse the hub->leds work_struct for our own purposes */
-               INIT_WORK(&hub->leds, hub_remove_children_work, hub);
-               schedule_work(&hub->leds);
-               usb_get_dev(hdev);
-       }
+       kfree(hub);
 }
 
 static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -917,26 +919,6 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
        }
 }
 
-/* caller has locked the hub device */
-static void hub_pre_reset(struct usb_hub *hub)
-{
-       struct usb_device *hdev = hub->hdev;
-       int i;
-
-       for (i = 0; i < hdev->maxchild; ++i) {
-               if (hdev->children[i])
-                       usb_disconnect(&hdev->children[i]);
-       }
-       hub_quiesce(hub);
-}
-
-/* caller has locked the hub device */
-static void hub_post_reset(struct usb_hub *hub)
-{
-       hub_activate(hub);
-       hub_power_on(hub);
-}
-
 
 /* grab device/port lock, returning index of that port (zero based).
  * protects the upstream link used by this device from concurrent
@@ -964,24 +946,21 @@ static int locktree(struct usb_device *udev)
        t = locktree(hdev);
        if (t < 0)
                return t;
-       for (t = 0; t < hdev->maxchild; t++) {
-               if (hdev->children[t] == udev) {
-                       /* everything is fail-fast once disconnect
-                        * processing starts
-                        */
-                       if (udev->state == USB_STATE_NOTATTACHED)
-                               break;
 
-                       /* when everyone grabs locks top->bottom,
-                        * non-overlapping work may be concurrent
-                        */
-                       down(&udev->serialize);
-                       up(&hdev->serialize);
-                       return t + 1;
-               }
+       /* 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 -ENODEV;
+       return udev->portnum;
 }
 
 static void recursively_mark_NOTATTACHED(struct usb_device *udev)
@@ -1039,6 +1018,39 @@ void usb_set_device_state(struct usb_device *udev,
 EXPORT_SYMBOL(usb_set_device_state);
 
 
+#ifdef CONFIG_PM
+
+/**
+ * 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.
+ */
+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");
+       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);
+}
+EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
+
+#endif
+
 static void choose_address(struct usb_device *udev)
 {
        int             devnum;
@@ -1099,16 +1111,10 @@ void usb_disconnect(struct usb_device **pdev)
         * this quiesces everyting except pending urbs.
         */
        usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-
-       /* lock the bus list on behalf of HCDs unregistering their root hubs */
-       if (!udev->parent) {
-               down(&usb_bus_list_lock);
-               usb_lock_device(udev);
-       } else
-               down(&udev->serialize);
-
        dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
 
+       usb_lock_device(udev);
+
        /* Free up all the children before we remove this device */
        for (i = 0; i < USB_MAXCHILDREN; i++) {
                if (udev->children[i])
@@ -1136,54 +1142,112 @@ void usb_disconnect(struct usb_device **pdev)
        *pdev = NULL;
        spin_unlock_irq(&device_state_lock);
 
-       if (!udev->parent) {
-               usb_unlock_device(udev);
-               up(&usb_bus_list_lock);
-       } else
-               up(&udev->serialize);
+       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 c, i;
+       int i;
+       u16 devstatus;
+       int bus_powered;
+       int num_configs;
+       struct usb_host_config *c, *best;
+
+       /* If this fails, assume the device is bus-powered */
+       devstatus = 0;
+       usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
+       le16_to_cpus(&devstatus);
+       bus_powered = ((devstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0);
+       dev_dbg(&udev->dev, "device is %s-powered\n",
+                       bus_powered ? "bus" : "self");
+
+       best = NULL;
+       c = udev->config;
+       num_configs = udev->descriptor.bNumConfigurations;
+       for (i = 0; i < num_configs; (i++, c++)) {
+               struct usb_interface_descriptor *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.
+                */
+#if 0
+               /* Rule out self-powered configs for a bus-powered device */
+               if (bus_powered && (c->desc.bmAttributes &
+                                       USB_CONFIG_ATT_SELFPOWER))
+                       continue;
+#endif
 
-       /* NOTE: this should interact with hub power budgeting */
+               /*
+                * 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.
+                */
 
-       c = udev->config[0].desc.bConfigurationValue;
-       if (udev->descriptor.bNumConfigurations != 1) {
-               for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
-                       struct usb_interface_descriptor *desc;
+               /* Rule out configs that draw too much bus current */
+               if (c->desc.bMaxPower * 2 > udev->bus_mA)
+                       continue;
 
-                       /* heuristic:  Linux is more likely to have class
-                        * drivers, so avoid vendor-specific interfaces.
-                        */
-                       desc = &udev->config[i].intf_cache[0]
-                                       ->altsetting->desc;
-                       if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
-                               continue;
-                       /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS.
-                        * MSFT needs this to be the first config; never use
-                        * it as the default unless Linux has host-side RNDIS.
-                        * A second config would ideally be CDC-Ethernet, but
-                        * may instead be the "vendor specific" CDC subset
-                        * long used by ARM Linux for sa1100 or pxa255.
-                        */
-                       if (desc->bInterfaceClass == USB_CLASS_COMM
-                                       && desc->bInterfaceSubClass == 2
-                                       && desc->bInterfaceProtocol == 0xff) {
-                               c = udev->config[1].desc.bConfigurationValue;
-                               continue;
-                       }
-                       c = udev->config[i].desc.bConfigurationValue;
+               /* 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->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->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 choices\n",
-                       c, udev->descriptor.bNumConfigurations);
+                       "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 c;
+       return i;
 }
 
 #ifdef DEBUG
@@ -1210,8 +1274,8 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
  *
  * This is called with devices which have been enumerated, but not yet
  * configured.  The device descriptor is available, but not descriptors
- * for any device configuration.  The caller must have locked udev and
- * either the parent hub (if udev is a normal device) or else the
+ * for any device configuration.  The caller must have locked either
+ * the parent hub (if udev is a normal device) or else the
  * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
  * udev has already been installed, but udev is not yet visible through
  * sysfs or other filesystem code.
@@ -1221,8 +1285,7 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
  *
  * This call is synchronous, and may not be used in an interrupt context.
  *
- * Only the hub driver should ever call this; root hub registration
- * uses it indirectly.
+ * Only the hub driver or root-hub registrar should ever call this.
  */
 int usb_new_device(struct usb_device *udev)
 {
@@ -1269,15 +1332,9 @@ int usb_new_device(struct usb_device *udev)
                                        le16_to_cpu(udev->config[0].desc.wTotalLength),
                                        USB_DT_OTG, (void **) &desc) == 0) {
                        if (desc->bmAttributes & USB_OTG_HNP) {
-                               unsigned                port1;
+                               unsigned                port1 = udev->portnum;
                                struct usb_device       *root = udev->parent;
                                
-                               for (port1 = 1; port1 <= root->maxchild;
-                                               port1++) {
-                                       if (root->children[port1-1] == udev)
-                                               break;
-                               }
-
                                dev_info(&udev->dev,
                                        "Dual-Role OTG device on %sHNP port\n",
                                        (port1 == bus->otg_port)
@@ -1331,27 +1388,27 @@ int usb_new_device(struct usb_device *udev)
        }
        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)
-               dev_warn(&udev->dev,
-                               "can't choose an initial configuration\n");
-       else {
+       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);
-                       usb_remove_sysfs_dev_files(udev);
-                       device_del(&udev->dev);
-                       goto fail;
+                       /* 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;
 
 fail:
@@ -1654,22 +1711,15 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
 int usb_suspend_device(struct usb_device *udev)
 {
 #ifdef CONFIG_USB_SUSPEND
-       int     port1, status;
-
-       port1 = locktree(udev);
-       if (port1 < 0)
-               return port1;
-
-       status = __usb_suspend_device(udev, port1);
-       usb_unlock_device(udev);
-       return status;
+       if (udev->state == USB_STATE_NOTATTACHED)
+               return -ENODEV;
+       return __usb_suspend_device(udev, udev->portnum);
 #else
        /* NOTE:  udev->state unchanged, it's not lying ... */
        udev->dev.power.power_state = PMSG_SUSPEND;
        return 0;
 #endif
 }
-EXPORT_SYMBOL_GPL(usb_suspend_device);
 
 /*
  * If the USB "suspend" state is in use (rather than "global suspend"),
@@ -1695,13 +1745,14 @@ static int finish_device_resume(struct usb_device *udev)
        usb_set_device_state(udev, udev->actconfig
                        ? USB_STATE_CONFIGURED
                        : USB_STATE_ADDRESS);
+       udev->dev.power.power_state = PMSG_ON;
 
        /* 10.5.4.5 says be sure devices in the tree are still there.
         * For now let's assume the device didn't go crazy on resume,
         * and device drivers will know about any resume quirks.
         */
        status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
-       if (status < 0)
+       if (status < 2)
                dev_dbg(&udev->dev,
                        "gone after usb resume? status %d\n",
                        status);
@@ -1710,7 +1761,7 @@ static int finish_device_resume(struct usb_device *udev)
                int             (*resume)(struct device *);
 
                le16_to_cpus(&devstatus);
-               if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)
+               if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
                                && udev->parent) {
                        status = usb_control_msg(udev,
                                        usb_sndctrlpipe(udev, 0),
@@ -1730,8 +1781,14 @@ static int finish_device_resume(struct usb_device *udev)
                 * may have a child resume event to deal with soon
                 */
                resume = udev->dev.bus->resume;
-               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)
-                       (void) resume(&udev->actconfig->interface[i]->dev);
+               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;
 
        } else if (udev->devnum <= 0) {
@@ -1814,11 +1871,10 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
  */
 int usb_resume_device(struct usb_device *udev)
 {
-       int     port1, status;
+       int     status;
 
-       port1 = locktree(udev);
-       if (port1 < 0)
-               return port1;
+       if (udev->state == USB_STATE_NOTATTACHED)
+               return -ENODEV;
 
 #ifdef CONFIG_USB_SUSPEND
        /* selective resume of one downstream hub-to-device port */
@@ -1827,7 +1883,7 @@ int usb_resume_device(struct usb_device *udev)
                        // NOTE swsusp may bork us, device state being wrong...
                        // NOTE this fails if parent is also suspended...
                        status = hub_port_resume(hdev_to_hub(udev->parent),
-                                       port1, udev);
+                                       udev->portnum, udev);
                } else
                        status = 0;
        } else
@@ -1837,13 +1893,11 @@ int usb_resume_device(struct usb_device *udev)
                dev_dbg(&udev->dev, "can't resume, status %d\n",
                        status);
 
-       usb_unlock_device(udev);
-
        /* rebind drivers that had no suspend() */
        if (status == 0) {
-               usb_lock_all_devices();
+               usb_unlock_device(udev);
                bus_rescan_devices(&usb_bus_type);
-               usb_unlock_all_devices();
+               usb_lock_device(udev);
        }
        return status;
 }
@@ -1857,14 +1911,14 @@ static int remote_wakeup(struct usb_device *udev)
        /* don't repeat RESUME sequence if this device
         * was already woken up by some other task
         */
-       down(&udev->serialize);
+       usb_lock_device(udev);
        if (udev->state == USB_STATE_SUSPENDED) {
                dev_dbg(&udev->dev, "RESUME (wakeup)\n");
                /* TRSMRCY = 10 msec */
                msleep(10);
                status = finish_device_resume(udev);
        }
-       up(&udev->serialize);
+       usb_unlock_device(udev);
 #endif
        return status;
 }
@@ -1965,7 +2019,7 @@ static int hub_resume(struct usb_interface *intf)
 
                if (!udev || status < 0)
                        continue;
-               down (&udev->serialize);
+               usb_lock_device(udev);
                if (portstat & USB_PORT_STAT_SUSPEND)
                        status = hub_port_resume(hub, port1, udev);
                else {
@@ -1976,7 +2030,7 @@ static int hub_resume(struct usb_interface *intf)
                                hub_port_logical_disconnect(hub, port1);
                        }
                }
-               up(&udev->serialize);
+               usb_unlock_device(udev);
        }
        }
 #endif
@@ -2360,39 +2414,36 @@ hub_power_remaining (struct usb_hub *hub)
 {
        struct usb_device *hdev = hub->hdev;
        int remaining;
-       unsigned i;
+       int port1;
 
-       remaining = hub->power_budget;
-       if (!remaining)         /* self-powered */
+       if (!hub->limited_power)
                return 0;
 
-       for (i = 0; i < hdev->maxchild; i++) {
-               struct usb_device       *udev = hdev->children[i];
-               int                     delta, ceiling;
+       remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;
+       for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+               struct usb_device       *udev = hdev->children[port1 - 1];
+               int                     delta;
 
                if (!udev)
                        continue;
 
-               /* 100mA per-port ceiling, or 8mA for OTG ports */
-               if (i != (udev->bus->otg_port - 1) || hdev->parent)
-                       ceiling = 50;
-               else
-                       ceiling = 4;
-
+               /* Unconfigured devices may not use more than 100mA,
+                * or 8mA for OTG ports */
                if (udev->actconfig)
-                       delta = udev->actconfig->desc.bMaxPower;
+                       delta = udev->actconfig->desc.bMaxPower * 2;
+               else if (port1 != udev->bus->otg_port || hdev->parent)
+                       delta = 100;
                else
-                       delta = ceiling;
-               // dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta);
-               if (delta > ceiling)
-                       dev_warn(&udev->dev, "%dmA over %dmA budget!\n",
-                               2 * (delta - ceiling), 2 * ceiling);
+                       delta = 8;
+               if (delta > hub->mA_per_port)
+                       dev_warn(&udev->dev, "%dmA is over %umA budget "
+                                       "for port %d!\n",
+                                       delta, hub->mA_per_port, port1);
                remaining -= delta;
        }
        if (remaining < 0) {
-               dev_warn(hub->intfdev,
-                       "%dmA over power budget!\n",
-                       -2 * remaining);
+               dev_warn(hub->intfdev, "%dmA over power budget!\n",
+                       - remaining);
                remaining = 0;
        }
        return remaining;
@@ -2487,7 +2538,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 
                usb_set_device_state(udev, USB_STATE_POWERED);
                udev->speed = USB_SPEED_UNKNOWN;
+               udev->bus_mA = hub->mA_per_port;
+
                /* set the address */
                choose_address(udev);
                if (udev->devnum <= 0) {
@@ -2507,16 +2559,16 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                 * on the parent.
                 */
                if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
-                               && hub->power_budget) {
+                               && udev->bus_mA <= 100) {
                        u16     devstat;
 
                        status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
                                        &devstat);
-                       if (status < 0) {
+                       if (status < 2) {
                                dev_dbg(&udev->dev, "get status %d ?\n", status);
                                goto loop_disable;
                        }
-                       cpu_to_le16s(&devstat);
+                       le16_to_cpus(&devstat);
                        if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
                                dev_err(&udev->dev,
                                        "can't connect bus-powered hub "
@@ -2541,7 +2593,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                 * udev becomes globally accessible, although presumably
                 * no one will look at it until hdev is unlocked.
                 */
-               down (&udev->serialize);
                status = 0;
 
                /* We mustn't add new devices if the parent hub has
@@ -2565,15 +2616,12 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        }
                }
 
-               up (&udev->serialize);
                if (status)
                        goto loop_disable;
 
                status = hub_power_remaining(hub);
                if (status)
-                       dev_dbg(hub_dev,
-                               "%dmA power budget left\n",
-                               2 * status);
+                       dev_dbg(hub_dev, "%dmA power budget left\n", status);
 
                return;
 
@@ -2649,6 +2697,8 @@ static void hub_events(void)
                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
@@ -2662,7 +2712,7 @@ static void hub_events(void)
 
                /* If the hub has died, clean up after it */
                if (hdev->state == USB_STATE_NOTATTACHED) {
-                       hub_pre_reset(hub);
+                       hub_pre_reset(hub, 0);
                        goto loop;
                }
 
@@ -2785,6 +2835,11 @@ static void hub_events(void)
                        if (hubchange & HUB_CHANGE_LOCAL_POWER) {
                                dev_dbg (hub_dev, "power change\n");
                                clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
+                               if (hubstatus & HUB_STATUS_LOCAL_POWER)
+                                       /* FIXME: Is this always true? */
+                                       hub->limited_power = 0;
+                               else
+                                       hub->limited_power = 1;
                        }
                        if (hubchange & HUB_CHANGE_OVERCURRENT) {
                                dev_dbg (hub_dev, "overcurrent change\n");
@@ -2833,7 +2888,6 @@ static struct usb_device_id hub_id_table [] = {
 MODULE_DEVICE_TABLE (usb, hub_id_table);
 
 static struct usb_driver hub_driver = {
-       .owner =        THIS_MODULE,
        .name =         "hub",
        .probe =        hub_probe,
        .disconnect =   hub_disconnect,
@@ -2945,7 +2999,8 @@ int usb_reset_device(struct usb_device *udev)
        struct usb_hub                  *parent_hub;
        struct usb_device_descriptor    descriptor = udev->descriptor;
        struct usb_hub                  *hub = NULL;
-       int                             i, ret = 0, port1 = -1;
+       int                             i, ret = 0;
+       int                             port1 = udev->portnum;
 
        if (udev->state == USB_STATE_NOTATTACHED ||
                        udev->state == USB_STATE_SUSPENDED) {
@@ -2959,18 +3014,6 @@ int usb_reset_device(struct usb_device *udev)
                dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
                return -EISDIR;
        }
-
-       for (i = 0; i < parent_hdev->maxchild; i++)
-               if (parent_hdev->children[i] == udev) {
-                       port1 = i + 1;
-                       break;
-               }
-
-       if (port1 < 0) {
-               /* If this ever happens, it's very bad */
-               dev_err(&udev->dev, "Can't locate device's port!\n");
-               return -ENOENT;
-       }
        parent_hub = hdev_to_hub(parent_hdev);
 
        /* If we're resetting an active hub, take some special actions */
@@ -2978,7 +3021,7 @@ int usb_reset_device(struct usb_device *udev)
                        udev->actconfig->interface[0]->dev.driver ==
                                &hub_driver.driver &&
                        (hub = hdev_to_hub(udev)) != NULL) {
-               hub_pre_reset(hub);
+               hub_pre_reset(hub, 0);
        }
 
        set_bit(port1, parent_hub->busy_bits);