aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2005-11-23 11:24:21 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2005-11-23 11:24:21 -0800
commit80129314f40abcb6f9b25b9a32784960cbaf3af2 (patch)
tree2d6cffbbcece44827d8907c6dd1e26dc04f8f09e /usb
parent59caddd1a7f16143e3a427ecc68566e98d599b0b (diff)
downloadpatches-80129314f40abcb6f9b25b9a32784960cbaf3af2.tar.gz
more usb stuff
Diffstat (limited to 'usb')
-rw-r--r--usb/usb-store-port-number-in-usb_device.patch181
-rw-r--r--usb/usbcore-consider-power-budget-when-choosing-configuration.patch454
2 files changed, 635 insertions, 0 deletions
diff --git a/usb/usb-store-port-number-in-usb_device.patch b/usb/usb-store-port-number-in-usb_device.patch
new file mode 100644
index 00000000000000..bcabafef8b220c
--- /dev/null
+++ b/usb/usb-store-port-number-in-usb_device.patch
@@ -0,0 +1,181 @@
+From stern@rowland.harvard.edu Wed Nov 23 10:03:10 2005
+Date: Wed, 23 Nov 2005 12:09:52 -0500 (EST)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: USB: Store port number in usb_device
+Message-ID: <Pine.LNX.4.44L0.0511231204280.12957-100000@iolanthe.rowland.org>
+
+This patch (as610) adds a field to struct usb_device to store the device's
+port number. This allows us to remove several loops in the hub driver
+(searching for a particular device among all the entries in the parent's
+array of children).
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/hub.c | 79 +++++++++++--------------------------------------
+ drivers/usb/core/usb.c | 1
+ include/linux/usb.h | 1
+ 3 files changed, 20 insertions(+), 61 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/core/hub.c
++++ gregkh-2.6/drivers/usb/core/hub.c
+@@ -946,24 +946,21 @@ static int locktree(struct usb_device *u
+ 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
+- */
+- usb_lock_device(udev);
+- usb_unlock_device(hdev);
+- 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)
+@@ -1335,15 +1332,9 @@ int usb_new_device(struct usb_device *ud
+ 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)
+@@ -1720,22 +1711,9 @@ static int __usb_suspend_device (struct
+ int usb_suspend_device(struct usb_device *udev)
+ {
+ #ifdef CONFIG_USB_SUSPEND
+- int port1;
+-
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+- if (!udev->parent)
+- port1 = 0;
+- else {
+- for (port1 = udev->parent->maxchild; port1 > 0; --port1) {
+- if (udev->parent->children[port1-1] == udev)
+- break;
+- }
+- if (port1 == 0)
+- return -ENODEV;
+- }
+-
+- return __usb_suspend_device(udev, port1);
++ return __usb_suspend_device(udev, udev->portnum);
+ #else
+ /* NOTE: udev->state unchanged, it's not lying ... */
+ udev->dev.power.power_state = PMSG_SUSPEND;
+@@ -1893,20 +1871,10 @@ hub_port_resume(struct usb_hub *hub, int
+ */
+ int usb_resume_device(struct usb_device *udev)
+ {
+- int port1, status;
++ int status;
+
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+- if (!udev->parent)
+- port1 = 0;
+- else {
+- for (port1 = udev->parent->maxchild; port1 > 0; --port1) {
+- if (udev->parent->children[port1-1] == udev)
+- break;
+- }
+- if (port1 == 0)
+- return -ENODEV;
+- }
+
+ #ifdef CONFIG_USB_SUSPEND
+ /* selective resume of one downstream hub-to-device port */
+@@ -1915,7 +1883,7 @@ int usb_resume_device(struct usb_device
+ // 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
+@@ -3029,7 +2997,8 @@ int usb_reset_device(struct usb_device *
+ 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) {
+@@ -3043,18 +3012,6 @@ int usb_reset_device(struct usb_device *
+ 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 */
+--- gregkh-2.6.orig/include/linux/usb.h
++++ gregkh-2.6/include/linux/usb.h
+@@ -348,6 +348,7 @@ struct usb_device {
+ char **rawdescriptors; /* Raw descriptors for each config */
+
+ unsigned short bus_mA; /* Current available from the bus */
++ u8 portnum; /* Parent port number (origin 1) */
+
+ int have_langid; /* whether string_langid is valid */
+ int string_langid; /* language ID for strings */
+--- gregkh-2.6.orig/drivers/usb/core/usb.c
++++ gregkh-2.6/drivers/usb/core/usb.c
+@@ -436,6 +436,7 @@ usb_alloc_dev(struct usb_device *parent,
+ /* hub driver sets up TT records */
+ }
+
++ dev->portnum = port1;
+ dev->bus = bus;
+ dev->parent = parent;
+ INIT_LIST_HEAD(&dev->filelist);
diff --git a/usb/usbcore-consider-power-budget-when-choosing-configuration.patch b/usb/usbcore-consider-power-budget-when-choosing-configuration.patch
new file mode 100644
index 00000000000000..58f3ff10802a7f
--- /dev/null
+++ b/usb/usbcore-consider-power-budget-when-choosing-configuration.patch
@@ -0,0 +1,454 @@
+From stern@rowland.harvard.edu Wed Nov 23 10:02:34 2005
+Date: Wed, 23 Nov 2005 12:03:12 -0500 (EST)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: usbcore: Consider power budget when choosing configuration
+Message-ID: <Pine.LNX.4.44L0.0511231128400.4477-100000@iolanthe.rowland.org>
+
+This patch (as609) changes the way we keep track of power budgeting for
+USB hubs and devices, and it updates the choose_configuration routine to
+take this information into account. (This is something we should have
+been doing all along.) A new field in struct usb_device holds the amount
+of bus current available from the upstream port, and the usb_hub structure
+keeps track of the current available for each downstream port.
+
+Two new rules for configuration selection are added:
+
+ Don't select a self-powered configuration when only bus power
+ is available.
+
+ Don't select a configuration requiring more bus power than is
+ available.
+
+However the first rule is #if-ed out, because I found that the internal
+hub in my HP USB keyboard claims that its only configuration is
+self-powered. The rule would prevent the configuration from being chosen,
+leaving the hub & keyboard unconfigured. Since similar descriptor errors
+may turn out to be fairly common, it seemed wise not to include a rule
+that would break automatic configuration unnecessarily for such devices.
+
+The second rule may also trigger unnecessarily, although this should be
+less common. More likely it will annoy people by sometimes failing to
+accept configurations that should never have been chosen in the first
+place.
+
+The patch also changes usbcore's reaction when no configuration is
+suitable. Instead of raising an error and rejecting the device, now
+the core will simply leave the device unconfigured. People can always
+work around such problems by installing configurations manually through
+sysfs.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/hcd.c | 5
+ drivers/usb/core/hub.c | 231 +++++++++++++++++++++++++++++----------------
+ drivers/usb/core/hub.h | 3
+ drivers/usb/core/message.c | 6 +
+ include/linux/usb.h | 2
+ 5 files changed, 165 insertions(+), 82 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/core/hub.c
++++ gregkh-2.6/drivers/usb/core/hub.c
+@@ -702,26 +702,40 @@ static int hub_configure(struct usb_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;
+- }
+- if (hub->power_budget)
+- dev_dbg(hub_dev, "%dmA bus power budget for children\n",
+- hub->power_budget * 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->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) {
+@@ -1136,45 +1150,107 @@ void usb_disconnect(struct usb_device **
+ 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
+@@ -1327,17 +1403,13 @@ int usb_new_device(struct usb_device *ud
+ * 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. */
+ }
+ }
+
+@@ -1702,7 +1774,7 @@ static int finish_device_resume(struct u
+ * 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);
+@@ -1711,7 +1783,7 @@ static int finish_device_resume(struct u
+ 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),
+@@ -2374,39 +2446,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;
+@@ -2501,7 +2570,8 @@ static void hub_port_connect_change(stru
+
+ 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) {
+@@ -2521,16 +2591,16 @@ static void hub_port_connect_change(stru
+ * 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 "
+@@ -2583,9 +2653,7 @@ static void hub_port_connect_change(stru
+
+ 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;
+
+@@ -2797,6 +2865,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");
+--- gregkh-2.6.orig/drivers/usb/core/hub.h
++++ gregkh-2.6/drivers/usb/core/hub.h
+@@ -220,8 +220,9 @@ struct usb_hub {
+ struct usb_hub_descriptor *descriptor; /* class descriptor */
+ struct usb_tt tt; /* Transaction Translator */
+
+- u8 power_budget; /* in 2mA units; or zero */
++ unsigned mA_per_port; /* current for each child */
+
++ unsigned limited_power:1;
+ unsigned quiescing:1;
+ unsigned activating:1;
+ unsigned resume_root_hub:1;
+--- gregkh-2.6.orig/drivers/usb/core/hcd.c
++++ gregkh-2.6/drivers/usb/core/hcd.c
+@@ -1820,8 +1820,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
+ retval = -ENOMEM;
+ goto err_allocate_root_hub;
+ }
+- rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
+- USB_SPEED_FULL;
+
+ /* Although in principle hcd->driver->start() might need to use rhdev,
+ * none of the current drivers do.
+@@ -1839,6 +1837,9 @@ int usb_add_hcd(struct usb_hcd *hcd,
+ dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
+ hcd->remote_wakeup = hcd->can_wakeup;
+
++ rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
++ USB_SPEED_FULL;
++ rhdev->bus_mA = min(500u, hcd->power_budget);
+ if ((retval = register_root_hub(rhdev, hcd)) != 0)
+ goto err_register_root_hub;
+
+--- gregkh-2.6.orig/include/linux/usb.h
++++ gregkh-2.6/include/linux/usb.h
+@@ -347,6 +347,8 @@ struct usb_device {
+
+ char **rawdescriptors; /* Raw descriptors for each config */
+
++ unsigned short bus_mA; /* Current available from the bus */
++
+ int have_langid; /* whether string_langid is valid */
+ int string_langid; /* language ID for strings */
+
+--- gregkh-2.6.orig/drivers/usb/core/message.c
++++ gregkh-2.6/drivers/usb/core/message.c
+@@ -1387,6 +1387,12 @@ free_interfaces:
+ if (dev->state != USB_STATE_ADDRESS)
+ usb_disable_device (dev, 1); // Skip ep0
+
++ n = dev->bus_mA - cp->desc.bMaxPower * 2;
++ if (n < 0)
++ dev_warn(&dev->dev, "new config #%d exceeds power "
++ "limit by %dmA\n",
++ configuration, -n);
++
+ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0)