usb/core: consider link speed while looking at bMaxPower
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Tue, 18 Dec 2012 14:25:46 +0000 (15:25 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 12 Jan 2013 00:16:01 +0000 (16:16 -0800)
The USB 2.0 specification says that bMaxPower is the maximum power
consumption expressed in 2 mA units and the USB 3.0 specification says
that it is expressed in 8 mA units.
This patch adds a helper function usb_get_max_power() which computes the
value based on config & usb_device's speed value. The the device descriptor
dump computes the value on its own.

Cc: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/devices.c
drivers/usb/core/generic.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/sysfs.c
drivers/usb/core/usb.h

index cbacea9..e33224e 100644 (file)
@@ -316,17 +316,23 @@ static char *usb_dump_iad_descriptor(char *start, char *end,
  */
 static char *usb_dump_config_descriptor(char *start, char *end,
                                const struct usb_config_descriptor *desc,
-                               int active)
+                               int active, int speed)
 {
+       int mul;
+
        if (start > end)
                return start;
+       if (speed == USB_SPEED_SUPER)
+               mul = 8;
+       else
+               mul = 2;
        start += sprintf(start, format_config,
                         /* mark active/actual/current cfg. */
                         active ? '*' : ' ',
                         desc->bNumInterfaces,
                         desc->bConfigurationValue,
                         desc->bmAttributes,
-                        desc->bMaxPower * 2);
+                        desc->bMaxPower * mul);
        return start;
 }
 
@@ -342,7 +348,8 @@ static char *usb_dump_config(int speed, char *start, char *end,
        if (!config)
                /* getting these some in 2.3.7; none in 2.3.6 */
                return start + sprintf(start, "(null Cfg. desc.)\n");
-       start = usb_dump_config_descriptor(start, end, &config->desc, active);
+       start = usb_dump_config_descriptor(start, end, &config->desc, active,
+                       speed);
        for (i = 0; i < USB_MAXIADS; i++) {
                if (config->intf_assoc[i] == NULL)
                        break;
index eff2010..271e761 100644 (file)
@@ -100,7 +100,7 @@ int usb_choose_configuration(struct usb_device *udev)
                 */
 
                /* Rule out configs that draw too much bus current */
-               if (c->desc.bMaxPower * 2 > udev->bus_mA) {
+               if (usb_get_max_power(udev, c) > udev->bus_mA) {
                        insufficient_power++;
                        continue;
                }
index ae10862..1291330 100644 (file)
@@ -4211,7 +4211,7 @@ hub_power_remaining (struct usb_hub *hub)
                /* Unconfigured devices may not use more than 100mA,
                 * or 8mA for OTG ports */
                if (udev->actconfig)
-                       delta = udev->actconfig->desc.bMaxPower * 2;
+                       delta = usb_get_max_power(udev, udev->actconfig);
                else if (port1 != udev->bus->otg_port || hdev->parent)
                        delta = 100;
                else
index 131f736..444d30e 100644 (file)
@@ -1751,7 +1751,7 @@ free_interfaces:
                        }
                }
 
-               i = dev->bus_mA - cp->desc.bMaxPower * 2;
+               i = dev->bus_mA - usb_get_max_power(dev, cp);
                if (i < 0)
                        dev_warn(&dev->dev, "new config #%d exceeds power "
                                        "limit by %dmA\n",
index 818e4a0..3f81a3d 100644 (file)
@@ -17,7 +17,7 @@
 #include "usb.h"
 
 /* Active configuration fields */
-#define usb_actconfig_show(field, multiplier, format_string)           \
+#define usb_actconfig_show(field, format_string)                       \
 static ssize_t  show_##field(struct device *dev,                       \
                struct device_attribute *attr, char *buf)               \
 {                                                                      \
@@ -28,18 +28,31 @@ static ssize_t  show_##field(struct device *dev,                    \
        actconfig = udev->actconfig;                                    \
        if (actconfig)                                                  \
                return sprintf(buf, format_string,                      \
-                               actconfig->desc.field * multiplier);    \
+                               actconfig->desc.field);                 \
        else                                                            \
                return 0;                                               \
 }                                                                      \
 
-#define usb_actconfig_attr(field, multiplier, format_string)           \
-usb_actconfig_show(field, multiplier, format_string)                   \
-static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+#define usb_actconfig_attr(field, format_string)               \
+       usb_actconfig_show(field, format_string)                \
+       static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+usb_actconfig_attr(bNumInterfaces, "%2d\n")
+usb_actconfig_attr(bmAttributes, "%2x\n")
 
-usb_actconfig_attr(bNumInterfaces, 1, "%2d\n")
-usb_actconfig_attr(bmAttributes, 1, "%2x\n")
-usb_actconfig_attr(bMaxPower, 2, "%3dmA\n")
+static ssize_t show_bMaxPower(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct usb_device *udev;
+       struct usb_host_config *actconfig;
+
+       udev = to_usb_device(dev);
+       actconfig = udev->actconfig;
+       if (!actconfig)
+               return 0;
+       return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+}
+static DEVICE_ATTR(bMaxPower, S_IRUGO, show_bMaxPower, NULL);
 
 static ssize_t show_configuration_string(struct device *dev,
                struct device_attribute *attr, char *buf)
@@ -56,7 +69,7 @@ static ssize_t show_configuration_string(struct device *dev,
 static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
 
 /* configuration value is always present, and r/w */
-usb_actconfig_show(bConfigurationValue, 1, "%u\n");
+usb_actconfig_show(bConfigurationValue, "%u\n");
 
 static ssize_t
 set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
index 1c528c1..fb7d8fc 100644 (file)
@@ -38,6 +38,15 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_choose_configuration(struct usb_device *udev);
 
+static inline unsigned usb_get_max_power(struct usb_device *udev,
+               struct usb_host_config *c)
+{
+       /* SuperSpeed power is in 8 mA units; others are in 2 mA units */
+       unsigned mul = (udev->speed == USB_SPEED_SUPER ? 8 : 2);
+
+       return c->desc.bMaxPower * mul;
+}
+
 extern void usb_kick_khubd(struct usb_device *dev);
 extern int usb_match_one_id_intf(struct usb_device *dev,
                                 struct usb_host_interface *intf,