summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/composite.c
diff options
context:
space:
mode:
authorDanny Segal <dsegal@codeaurora.org>2014-07-28 18:08:33 +0300
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:02:44 -0700
commit190c05e664e74320bbf416dfcee4e227440eef1e (patch)
treedddf78c8d515d97377edf7d94d43ceda5bca5d55 /drivers/usb/gadget/composite.c
parent4571803aa1530b3fdfae0e8b1ad0e5df4372d873 (diff)
usb: gadget: Fix race condition between function wakeup and bus resume
When a SS-USB function wishes to wake up the USB bus, it needs to send a SS-USB function wakeup notification to the USB host after the bus is resumed. For this purpose a function wake up pending flag is used to notify the resume callback that a function wake up notification needs to be sent. However, sometimes there is a race condition in which the resume interrupt is fired before the function wakeup function is complete, and this leads to an incorrect state of the function wakeup pending flag. This patch resolves this issue by adding locks in the critical sections. CRs-fixed: 695399 Change-Id: I8f15ac0c433301d6364a49cb31577e30259aa0b9 Signed-off-by: Danny Segal <dsegal@codeaurora.org>
Diffstat (limited to 'drivers/usb/gadget/composite.c')
-rw-r--r--drivers/usb/gadget/composite.c79
1 files changed, 55 insertions, 24 deletions
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index b7ca27e095f9..aa538fc59cab 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -388,11 +388,13 @@ int usb_get_func_interface_id(struct usb_function *func)
return -ENODEV;
}
-int usb_func_wakeup(struct usb_function *func)
+static int _usb_func_wakeup(struct usb_function *func, bool use_pending_flag)
{
int ret;
- unsigned interface_id;
+ int interface_id;
+ unsigned long flags;
struct usb_gadget *gadget;
+ struct usb_composite_dev *cdev;
pr_debug("%s function wakeup\n",
func->name ? func->name : "");
@@ -411,6 +413,15 @@ int usb_func_wakeup(struct usb_function *func)
return -ENOTSUPP;
}
+ cdev = get_gadget_data(gadget);
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (use_pending_flag && !func->func_wakeup_pending) {
+ pr_debug("Pending flag is cleared - Function wakeup is cancelled.\n");
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ return 0;
+ }
+
ret = usb_get_func_interface_id(func);
if (ret < 0) {
ERROR(func->config->cdev,
@@ -421,25 +432,36 @@ int usb_func_wakeup(struct usb_function *func)
interface_id = ret;
ret = usb_gadget_func_wakeup(gadget, interface_id);
- if (ret) {
- 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 : "");
- func->func_wakeup_pending = true;
- ret = 0;
- } else {
- ERROR(func->config->cdev,
- "Failed to wake function %s from suspend state. interface id: %d, ret=%d. Canceling USB request.\n",
- func->name ? func->name : "",
- interface_id, ret);
- }
- return ret;
+ if (use_pending_flag)
+ func->func_wakeup_pending = false;
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
+ return ret;
+}
+
+int usb_func_wakeup(struct usb_function *func)
+{
+ int ret;
+
+ pr_debug("%s function wakeup\n",
+ func->name ? func->name : "");
+
+ ret = _usb_func_wakeup(func, false);
+ 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 : "");
+ func->func_wakeup_pending = true;
+ ret = 0;
+ } else if (ret < 0) {
+ ERROR(func->config->cdev,
+ "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
+ func->name ? func->name : "", ret);
}
- func->func_wakeup_pending = false;
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(usb_func_wakeup);
@@ -744,6 +766,7 @@ static void reset_config(struct usb_composite_dev *cdev)
/* USB 3.0 addition */
f->func_is_suspended = false;
f->func_wakeup_allowed = false;
+ f->func_wakeup_pending = false;
bitmap_zero(f->endpoints, 32);
}
@@ -2278,14 +2301,22 @@ void composite_resume(struct usb_gadget *gadget)
DBG(cdev, "resume\n");
if (cdev->driver->resume)
cdev->driver->resume(cdev);
+
if (cdev->config) {
list_for_each_entry(f, &cdev->config->functions, list) {
- if (f->func_wakeup_pending) {
- ret = usb_func_wakeup(f);
- if (ret)
- ERROR(cdev,
- "Failed to send function wakeup notification for the %s function. Error code: %d\n",
- f->name ? f->name : "", ret);
+ ret = _usb_func_wakeup(f, true);
+ 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 {
+ 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)