diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2005-11-23 11:24:21 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-11-23 11:24:21 -0800 |
commit | 80129314f40abcb6f9b25b9a32784960cbaf3af2 (patch) | |
tree | 2d6cffbbcece44827d8907c6dd1e26dc04f8f09e /usb | |
parent | 59caddd1a7f16143e3a427ecc68566e98d599b0b (diff) | |
download | patches-80129314f40abcb6f9b25b9a32784960cbaf3af2.tar.gz |
more usb stuff
Diffstat (limited to 'usb')
-rw-r--r-- | usb/usb-store-port-number-in-usb_device.patch | 181 | ||||
-rw-r--r-- | usb/usbcore-consider-power-budget-when-choosing-configuration.patch | 454 |
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 0000000000000..bcabafef8b220 --- /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 0000000000000..58f3ff10802a7 --- /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) |