summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/composite.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/composite.c')
-rw-r--r--drivers/usb/gadget/composite.c343
1 files changed, 278 insertions, 65 deletions
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index a5ebfa9a2f82..442d44278f33 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -23,6 +23,23 @@
#include <asm/unaligned.h>
#include "u_os_desc.h"
+#define SSUSB_GADGET_VBUS_DRAW 900 /* in mA */
+#define SSUSB_GADGET_VBUS_DRAW_UNITS 8
+#define HSUSB_GADGET_VBUS_DRAW_UNITS 2
+
+/*
+ * Based on enumerated USB speed, draw power with set_config and resume
+ * HSUSB: 500mA, SSUSB: 900mA
+ */
+#define USB_VBUS_DRAW(speed)\
+ (speed == USB_SPEED_SUPER ?\
+ SSUSB_GADGET_VBUS_DRAW : CONFIG_USB_GADGET_VBUS_DRAW)
+
+/* disable LPM by default */
+static bool disable_l1_for_hs = true;
+module_param(disable_l1_for_hs, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(disable_l1_for_hs,
+ "Disable support for L1 LPM for HS devices");
/**
* struct usb_os_string - represents OS String to be reported by a gadget
@@ -205,7 +222,7 @@ int usb_add_function(struct usb_configuration *config,
{
int value = -EINVAL;
- DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
+ DBG(config->cdev, "adding '%s'/%pK to config '%s'/%pK\n",
function->name, function,
config->label, config);
@@ -213,6 +230,7 @@ int usb_add_function(struct usb_configuration *config,
goto done;
function->config = config;
+ function->intf_id = -EINVAL;
list_add_tail(&function->list, &config->functions);
if (function->bind_deactivated) {
@@ -245,7 +263,7 @@ int usb_add_function(struct usb_configuration *config,
done:
if (value)
- DBG(config->cdev, "adding '%s'/%p --> %d\n",
+ DBG(config->cdev, "adding '%s'/%pK --> %d\n",
function->name, function, value);
return value;
}
@@ -361,6 +379,8 @@ int usb_interface_id(struct usb_configuration *config,
if (id < MAX_CONFIG_INTERFACES) {
config->interface[id] = function;
+ if (function->intf_id < 0)
+ function->intf_id = id;
config->next_interface_id = id + 1;
return id;
}
@@ -368,22 +388,110 @@ int usb_interface_id(struct usb_configuration *config,
}
EXPORT_SYMBOL_GPL(usb_interface_id);
+static int usb_func_wakeup_int(struct usb_function *func)
+{
+ int ret;
+ struct usb_gadget *gadget;
+
+ pr_debug("%s - %s function wakeup\n",
+ __func__, func->name ? func->name : "");
+
+ if (!func || !func->config || !func->config->cdev ||
+ !func->config->cdev->gadget)
+ return -EINVAL;
+
+ gadget = func->config->cdev->gadget;
+ if ((gadget->speed != USB_SPEED_SUPER) || !func->func_wakeup_allowed) {
+ DBG(func->config->cdev,
+ "Function Wakeup is not possible. speed=%u, func_wakeup_allowed=%u\n",
+ gadget->speed,
+ func->func_wakeup_allowed);
+
+ return -ENOTSUPP;
+ }
+
+ ret = usb_gadget_func_wakeup(gadget, func->intf_id);
+
+ return ret;
+}
+
+int usb_func_wakeup(struct usb_function *func)
+{
+ int ret;
+ unsigned long flags;
+
+ pr_debug("%s function wakeup\n",
+ func->name ? func->name : "");
+
+ spin_lock_irqsave(&func->config->cdev->lock, flags);
+ ret = usb_func_wakeup_int(func);
+ if (ret == -EAGAIN) {
+ DBG(func->config->cdev,
+ "Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n",
+ func->name ? func->name : "");
+ ret = 0;
+ } else if (ret < 0 && ret != -ENOTSUPP) {
+ ERROR(func->config->cdev,
+ "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
+ func->name ? func->name : "", ret);
+ }
+
+ spin_unlock_irqrestore(&func->config->cdev->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_func_wakeup);
+
+int usb_func_ep_queue(struct usb_function *func, struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp_flags)
+{
+ int ret;
+ struct usb_gadget *gadget;
+
+ if (!func || !func->config || !func->config->cdev ||
+ !func->config->cdev->gadget || !ep || !req) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("Function %s queueing new data into ep %u\n",
+ func->name ? func->name : "", ep->address);
+
+ gadget = func->config->cdev->gadget;
+
+ if (func->func_is_suspended && func->func_wakeup_allowed) {
+ ret = usb_gadget_func_wakeup(gadget, func->intf_id);
+ if (ret == -EAGAIN) {
+ pr_debug("bus suspended func wakeup for %s delayed until bus resume.\n",
+ func->name ? func->name : "");
+ } else if (ret < 0 && ret != -ENOTSUPP) {
+ pr_err("Failed to wake function %s from suspend state. ret=%d.\n",
+ func->name ? func->name : "", ret);
+ }
+ goto done;
+ }
+
+ if (func->func_is_suspended && !func->func_wakeup_allowed) {
+ ret = -ENOTSUPP;
+ goto done;
+ }
+
+ ret = usb_ep_queue(ep, req, gfp_flags);
+done:
+ return ret;
+}
+
static u8 encode_bMaxPower(enum usb_device_speed speed,
struct usb_configuration *c)
{
- unsigned val;
+ unsigned val = CONFIG_USB_GADGET_VBUS_DRAW;
- if (c->MaxPower)
- val = c->MaxPower;
- else
- val = CONFIG_USB_GADGET_VBUS_DRAW;
- if (!val)
- return 0;
switch (speed) {
case USB_SPEED_SUPER:
- return DIV_ROUND_UP(val, 8);
+ /* with super-speed report 900mA */
+ val = SSUSB_GADGET_VBUS_DRAW;
+ return (u8)(val / SSUSB_GADGET_VBUS_DRAW_UNITS);
default:
- return DIV_ROUND_UP(val, 2);
+ return DIV_ROUND_UP(val, HSUSB_GADGET_VBUS_DRAW_UNITS);
}
}
@@ -564,7 +672,8 @@ static int bos_desc(struct usb_composite_dev *cdev)
/*
* A SuperSpeed device shall include the USB2.0 extension descriptor
- * and shall support LPM when operating in USB2.0 HS mode.
+ * and shall support LPM when operating in USB2.0 HS mode, as well as
+ * a HS device when operating in USB2.1 HS mode.
*/
usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
bos->bNumDeviceCaps++;
@@ -574,33 +683,37 @@ static int bos_desc(struct usb_composite_dev *cdev)
usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT);
- /*
- * The Superspeed USB Capability descriptor shall be implemented by all
- * SuperSpeed devices.
- */
- ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
- bos->bNumDeviceCaps++;
- le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
- ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
- ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
- ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
- ss_cap->bmAttributes = 0; /* LTM is not supported yet */
- ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
- USB_FULL_SPEED_OPERATION |
- USB_HIGH_SPEED_OPERATION |
- USB_5GBPS_OPERATION);
- ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
-
- /* Get Controller configuration */
- if (cdev->gadget->ops->get_config_params)
- cdev->gadget->ops->get_config_params(&dcd_config_params);
- else {
- dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
- dcd_config_params.bU2DevExitLat =
- cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ if (gadget_is_superspeed(cdev->gadget)) {
+ /*
+ * The Superspeed USB Capability descriptor shall be
+ * implemented by all SuperSpeed devices.
+ */
+ ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
+ ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+ ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+ ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+ ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
+ USB_FULL_SPEED_OPERATION |
+ USB_HIGH_SPEED_OPERATION |
+ USB_5GBPS_OPERATION);
+ ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+
+ /* Get Controller configuration */
+ if (cdev->gadget->ops->get_config_params)
+ cdev->gadget->ops->get_config_params
+ (&dcd_config_params);
+ else {
+ dcd_config_params.bU1devExitLat =
+ USB_DEFAULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU2DevExitLat =
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ }
+ ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+ ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
}
- ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
- ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
return le16_to_cpu(bos->wTotalLength);
}
@@ -634,6 +747,11 @@ static void reset_config(struct usb_composite_dev *cdev)
if (f->disable)
f->disable(f);
+ /* USB 3.0 addition */
+ f->func_is_suspended = false;
+ f->func_wakeup_allowed = false;
+ f->func_wakeup_pending = false;
+
bitmap_zero(f->endpoints, 32);
}
cdev->config = NULL;
@@ -646,9 +764,18 @@ static int set_config(struct usb_composite_dev *cdev,
struct usb_gadget *gadget = cdev->gadget;
struct usb_configuration *c = NULL;
int result = -EINVAL;
- unsigned power = gadget_is_otg(gadget) ? 8 : 100;
int tmp;
+ /*
+ * ignore 2nd time SET_CONFIGURATION
+ * only for same config value twice.
+ */
+ if (cdev->config && (cdev->config->bConfigurationValue == number)) {
+ DBG(cdev, "already in the same config with value %d\n",
+ number);
+ return 0;
+ }
+
if (number) {
list_for_each_entry(c, &cdev->configs, list) {
if (c->bConfigurationValue == number) {
@@ -680,6 +807,8 @@ static int set_config(struct usb_composite_dev *cdev,
usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
cdev->config = c;
+ c->num_ineps_used = 0;
+ c->num_outeps_used = 0;
/* Initialize all interfaces by setting them to altsetting zero. */
for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
@@ -697,6 +826,12 @@ static int set_config(struct usb_composite_dev *cdev,
*/
switch (gadget->speed) {
case USB_SPEED_SUPER:
+ if (!f->ss_descriptors) {
+ pr_err("%s(): No SS desc for function:%s\n",
+ __func__, f->name);
+ usb_gadget_set_state(gadget, USB_STATE_ADDRESS);
+ return -EINVAL;
+ }
descriptors = f->ss_descriptors;
break;
case USB_SPEED_HIGH:
@@ -717,11 +852,15 @@ static int set_config(struct usb_composite_dev *cdev,
addr = ((ep->bEndpointAddress & 0x80) >> 3)
| (ep->bEndpointAddress & 0x0f);
set_bit(addr, f->endpoints);
+ if (usb_endpoint_dir_in(ep))
+ c->num_ineps_used++;
+ else
+ c->num_outeps_used++;
}
result = f->set_alt(f, tmp, 0);
if (result < 0) {
- DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
+ DBG(cdev, "interface %d (%s/%pK) alt 0 --> %d\n",
tmp, f->name, f, result);
reset_config(cdev);
@@ -738,10 +877,8 @@ 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;
done:
- usb_gadget_vbus_draw(gadget, power);
+ usb_gadget_vbus_draw(gadget, USB_VBUS_DRAW(gadget->speed));
if (result >= 0 && cdev->delayed_status)
result = USB_GADGET_DELAYED_STATUS;
return result;
@@ -796,7 +933,7 @@ int usb_add_config(struct usb_composite_dev *cdev,
if (!bind)
goto done;
- DBG(cdev, "adding config #%u '%s'/%p\n",
+ DBG(cdev, "adding config #%u '%s'/%pK\n",
config->bConfigurationValue,
config->label, config);
@@ -813,7 +950,7 @@ int usb_add_config(struct usb_composite_dev *cdev,
struct usb_function, list);
list_del(&f->list);
if (f->unbind) {
- DBG(cdev, "unbind function '%s'/%p\n",
+ DBG(cdev, "unbind function '%s'/%pK\n",
f->name, f);
f->unbind(config, f);
/* may free memory for "f" */
@@ -824,7 +961,7 @@ int usb_add_config(struct usb_composite_dev *cdev,
} else {
unsigned i;
- DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
+ DBG(cdev, "cfg %d/%pK speeds:%s%s%s\n",
config->bConfigurationValue, config,
config->superspeed ? " super" : "",
config->highspeed ? " high" : "",
@@ -839,7 +976,7 @@ int usb_add_config(struct usb_composite_dev *cdev,
if (!f)
continue;
- DBG(cdev, " interface %d = %s/%p\n",
+ DBG(cdev, " interface %d = %s/%pK\n",
i, f->name, f);
}
}
@@ -865,14 +1002,14 @@ static void remove_config(struct usb_composite_dev *cdev,
struct usb_function, list);
list_del(&f->list);
if (f->unbind) {
- DBG(cdev, "unbind function '%s'/%p\n", f->name, f);
+ DBG(cdev, "unbind function '%s'/%pK\n", f->name, f);
f->unbind(config, f);
/* may free memory for "f" */
}
}
list_del(&config->list);
if (config->unbind) {
- DBG(cdev, "unbind config '%s'/%p\n", config->label, config);
+ DBG(cdev, "unbind config '%s'/%pK\n", config->label, config);
config->unbind(config);
/* may free memory for "c" */
}
@@ -1280,7 +1417,7 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
else if (cdev->os_desc_req == req)
cdev->os_desc_pending = false;
else
- WARN(1, "unknown request %p\n", req);
+ WARN(1, "unknown request %pK\n", req);
}
static int composite_ep0_queue(struct usb_composite_dev *cdev,
@@ -1295,7 +1432,7 @@ static int composite_ep0_queue(struct usb_composite_dev *cdev,
else if (cdev->os_desc_req == req)
cdev->os_desc_pending = true;
else
- WARN(1, "unknown request %p\n", req);
+ WARN(1, "unknown request %pK\n", req);
}
return ret;
@@ -1500,17 +1637,24 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_DT_DEVICE:
cdev->desc.bNumConfigurations =
count_configs(cdev, USB_DT_DEVICE);
+ if (cdev->desc.bNumConfigurations == 0) {
+ pr_err("%s:config is not active. send stall\n",
+ __func__);
+ break;
+ }
+
cdev->desc.bMaxPacketSize0 =
cdev->gadget->ep0->maxpacket;
+ cdev->desc.bcdUSB = cpu_to_le16(0x0200);
if (gadget_is_superspeed(gadget)) {
if (gadget->speed >= USB_SPEED_SUPER) {
- cdev->desc.bcdUSB = cpu_to_le16(0x0300);
+ cdev->desc.bcdUSB = cpu_to_le16(0x0310);
cdev->desc.bMaxPacketSize0 = 9;
- } else {
+ } else if (!disable_l1_for_hs) {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+ DBG(cdev,
+ "Config HS device with LPM(L1)\n");
}
- } else {
- cdev->desc.bcdUSB = cpu_to_le16(0x0200);
}
value = min(w_length, (u16) sizeof cdev->desc);
@@ -1520,7 +1664,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (!gadget_is_dualspeed(gadget) ||
gadget->speed >= USB_SPEED_SUPER)
break;
+ spin_lock(&cdev->lock);
device_qual(cdev);
+ spin_unlock(&cdev->lock);
value = min_t(int, w_length,
sizeof(struct usb_qualifier_descriptor));
break;
@@ -1530,18 +1676,24 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
break;
/* FALLTHROUGH */
case USB_DT_CONFIG:
+ spin_lock(&cdev->lock);
value = config_desc(cdev, w_value);
+ spin_unlock(&cdev->lock);
if (value >= 0)
value = min(w_length, (u16) value);
break;
case USB_DT_STRING:
+ spin_lock(&cdev->lock);
value = get_string(cdev, req->buf,
w_index, w_value & 0xff);
+ spin_unlock(&cdev->lock);
if (value >= 0)
value = min(w_length, (u16) value);
break;
case USB_DT_BOS:
- if (gadget_is_superspeed(gadget)) {
+ if ((gadget_is_superspeed(gadget) &&
+ (gadget->speed >= USB_SPEED_SUPER))
+ || !disable_l1_for_hs) {
value = bos_desc(cdev);
value = min(w_length, (u16) value);
}
@@ -1687,8 +1839,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (!f)
break;
value = 0;
- if (f->func_suspend)
- value = f->func_suspend(f, w_index >> 8);
+ if (f->func_suspend) {
+ const u8 suspend_opt = w_index >> 8;
+
+ value = f->func_suspend(f, suspend_opt);
+ DBG(cdev, "%s function: FUNCTION_SUSPEND(%u)",
+ f->name ? f->name : "", suspend_opt);
+ }
if (value < 0) {
ERROR(cdev,
"func_suspend() returned error %d\n",
@@ -1775,6 +1932,16 @@ unknown:
}
break;
}
+
+ if (value < 0) {
+ DBG(cdev, "%s: unhandled os desc request\n",
+ __func__);
+ DBG(cdev, "req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ return value;
+ }
+
req->length = value;
req->context = cdev;
req->zero = value < w_length;
@@ -1782,7 +1949,9 @@ unknown:
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
- composite_setup_complete(gadget->ep0, req);
+ if (value != -ESHUTDOWN)
+ composite_setup_complete(gadget->ep0,
+ req);
}
return value;
}
@@ -1815,6 +1984,8 @@ unknown:
break;
case USB_RECIP_ENDPOINT:
+ if (!cdev->config)
+ break;
endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
list_for_each_entry(f, &cdev->config->functions, list) {
if (test_bit(endp, f->endpoints))
@@ -1848,6 +2019,14 @@ try_fun_setup:
if (f->setup)
value = f->setup(f, ctrl);
}
+ if (value == USB_GADGET_DELAYED_STATUS) {
+ DBG(cdev,
+ "%s: interface %d (%s) requested delayed status\n",
+ __func__, intf, f->name);
+ cdev->delayed_status++;
+ DBG(cdev, "delayed_status count %d\n",
+ cdev->delayed_status);
+ }
goto done;
}
@@ -1861,7 +2040,8 @@ try_fun_setup:
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
- composite_setup_complete(gadget->ep0, req);
+ if (value != -ESHUTDOWN)
+ composite_setup_complete(gadget->ep0, req);
}
} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
WARN(cdev,
@@ -1893,6 +2073,10 @@ void composite_disconnect(struct usb_gadget *gadget)
reset_config(cdev);
if (cdev->driver->disconnect)
cdev->driver->disconnect(cdev);
+ if (cdev->delayed_status != 0) {
+ INFO(cdev, "delayed status mismatch..resetting\n");
+ cdev->delayed_status = 0;
+ }
spin_unlock_irqrestore(&cdev->lock, flags);
}
@@ -2062,14 +2246,18 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req);
kfree(cdev->os_desc_req->buf);
+ cdev->os_desc_req->buf = NULL;
usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
+ cdev->os_desc_req = NULL;
}
if (cdev->req) {
if (cdev->setup_pending)
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
kfree(cdev->req->buf);
+ cdev->req->buf = NULL;
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
+ cdev->req = NULL;
}
cdev->next_string_id = 0;
device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
@@ -2130,11 +2318,13 @@ void composite_suspend(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
+ unsigned long flags;
/* REVISIT: should we have config level
* suspend/resume callbacks?
*/
DBG(cdev, "suspend\n");
+ spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config) {
list_for_each_entry(f, &cdev->config->functions, list) {
if (f->suspend)
@@ -2145,6 +2335,7 @@ void composite_suspend(struct usb_gadget *gadget)
cdev->driver->suspend(cdev);
cdev->suspended = 1;
+ spin_unlock_irqrestore(&cdev->lock, flags);
usb_gadget_vbus_draw(gadget, 2);
}
@@ -2153,7 +2344,8 @@ void composite_resume(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
- u16 maxpower;
+ int ret;
+ unsigned long flags;
/* REVISIT: should we have config level
* suspend/resume callbacks?
@@ -2161,18 +2353,33 @@ void composite_resume(struct usb_gadget *gadget)
DBG(cdev, "resume\n");
if (cdev->driver->resume)
cdev->driver->resume(cdev);
+
+ spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config) {
list_for_each_entry(f, &cdev->config->functions, list) {
+ ret = usb_func_wakeup_int(f);
+ if (ret) {
+ if (ret == -EAGAIN) {
+ ERROR(f->config->cdev,
+ "Function wakeup for %s could not complete due to suspend state.\n",
+ f->name ? f->name : "");
+ break;
+ } else if (ret != -ENOTSUPP) {
+ ERROR(f->config->cdev,
+ "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
+ f->name ? f->name : "",
+ ret);
+ }
+ }
+
if (f->resume)
f->resume(f);
}
- maxpower = cdev->config->MaxPower;
-
- usb_gadget_vbus_draw(gadget, maxpower ?
- maxpower : CONFIG_USB_GADGET_VBUS_DRAW);
+ usb_gadget_vbus_draw(gadget, USB_VBUS_DRAW(gadget->speed));
}
+ spin_unlock_irqrestore(&cdev->lock, flags);
cdev->suspended = 0;
}
@@ -2264,7 +2471,13 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->delayed_status == 0) {
+ if (!cdev->config) {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&cdev->lock, flags);
WARN(cdev, "%s: Unexpected call\n", __func__);
+ return;
} else if (--cdev->delayed_status == 0) {
DBG(cdev, "%s: Completing delayed status\n", __func__);