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 sysfs interface &
the device descriptor dump compute the value on their own.

Cc: Sarah Sharp <sarah.a.sh...@linux.intel.com>
Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
Signed-off-by: Sebastian Andrzej Siewior <bige...@linutronix.de>
---
v1…v2:  - moved usb_get_max_power() to usb.h
        - removed the multiplier argument from the macro and implemented
          show_bMaxPower from scratch
 drivers/usb/core/devices.c |   13 ++++++++++---
 drivers/usb/core/generic.c |    2 +-
 drivers/usb/core/hub.c     |    2 +-
 drivers/usb/core/message.c |    2 +-
 drivers/usb/core/sysfs.c   |   37 ++++++++++++++++++++++++++++---------
 drivers/usb/core/usb.h     |    9 +++++++++
 6 files changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index cbacea9..e33224e 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -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;
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index eff2010..271e761 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -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;
                }
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a815fd2..4735425 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -4152,7 +4152,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
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 131f736..444d30e 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1751,7 +1751,7 @@ int usb_set_configuration(struct usb_device *dev, int 
configuration)
                        }
                }
 
-               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",
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 818e4a0..472fcb1 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -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,37 @@ 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")
+
+static ssize_t show_bMaxPower(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct usb_device *udev;
+       struct usb_host_config *actconfig;
+       unsigned multiplier;
 
-usb_actconfig_attr(bNumInterfaces, 1, "%2d\n")
-usb_actconfig_attr(bmAttributes, 1, "%2x\n")
-usb_actconfig_attr(bMaxPower, 2, "%3dmA\n")
+       udev = to_usb_device(dev);
+       if (udev->speed == USB_SPEED_SUPER)
+               multiplier = 8;
+       else
+               multiplier = 2;
+
+       actconfig = udev->actconfig;
+       if (!actconfig)
+               return 0;
+       return sprintf(buf, "%dmA\n", actconfig->desc.bMaxPower * multiplier);
+}
+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 +75,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,
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 1c528c1..fb7d8fc 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -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,
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to