@@ -438,9 +438,10 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
if (!val)
return 0;
if (speed < USB_SPEED_SUPER)
- return DIV_ROUND_UP(val, 2);
+ return DIV_ROUND_UP(min(val, 500U), 2);
else
- return DIV_ROUND_UP(val, 8);
+ /* USB 3.x supports 900mA, but that isn't divisible by 8... */
+ return DIV_ROUND_UP(min(val, 896U), 8);
}
static int config_buf(struct usb_configuration *config,
@@ -852,6 +853,10 @@ static int set_config(struct usb_composite_dev *cdev,
/* when we return, be sure our power usage is valid */
power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
+ if (gadget->speed < USB_SPEED_SUPER)
+ power = min(power, 500U);
+ else
+ power = min(power, 900U);
done:
usb_gadget_vbus_draw(gadget, power);
if (result >= 0 && cdev->delayed_status)
@@ -2289,6 +2294,10 @@ void composite_resume(struct usb_gadget *gadget)
}
maxpower = cdev->config->MaxPower;
+ if (gadget->speed < USB_SPEED_SUPER)
+ maxpower = min_t(u16, maxpower, 500U);
+ else
+ maxpower = min_t(u16, maxpower, 900U);
usb_gadget_vbus_draw(gadget, maxpower ?
maxpower : CONFIG_USB_GADGET_VBUS_DRAW);
USB 3.x SuperSpeed peripherals can draw up to 900mA of VBUS power when in configured state. However, if a configuration wanting to take advantage of this is added with MaxPower greater than 500 (currently possible if using a ConfigFS gadget) the composite driver fails to accommodate this for a couple reasons: - usb_gadget_vbus_draw() when called from set_config() and composite_resume() will be passed the MaxPower value without regard for the current connection speed, resulting in a violation for USB 2.0 since the max is 500mA. - the bMaxPower of the configuration descriptor would be incorrectly encoded, again if the connection speed is only at USB 2.0 or below, likely wrapping around UINT8_MAX since the 2mA multiplier corresponds to a maximum of 510mA. Fix these by adding checks against the current gadget->speed when the c->MaxPower value is used and appropriately limit based on whether it is currently at a low-/full-/high- or super- speed connection. Incidentally, 900 is not divisible by 8, so even for Superspeed the bMaxPower needs to be capped at 896mA, otherwise due to the round-up division a MaxPower of 900mA will result in an encoded value of 0x71. When a host stack (including Linux and Windows) enumerates this on a single port root hub, it reads this value back and decodes (multiplies by 8) to get 904mA which is strictly greater than 900mA that is typically budgeted for that port, causing it to reject the configuration. N.B. USB 3.2 Gen N x 2 allows for up to 1500mA but there doesn't seem to be any any peripheral controller supported by Linux that does two lane operation, so for now keeping the clamp at 900 should be fine. Signed-off-by: Jack Pham <jackp@codeaurora.org> --- v2: Fix typos in commit text and reworded the blurb about rounding drivers/usb/gadget/composite.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)