Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62b11b12 authored by Danny Segal's avatar Danny Segal
Browse files

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: default avatarDanny Segal <dsegal@codeaurora.org>
parent 545af185
Loading
Loading
Loading
Loading
+55 −24
Original line number Diff line number Diff line
@@ -363,11 +363,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 : "");
@@ -386,6 +388,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,
@@ -396,27 +407,38 @@ int usb_func_wakeup(struct usb_function *func)

	interface_id = ret;
	ret = usb_gadget_func_wakeup(gadget, interface_id);
	if (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 {
	} else if (ret < 0) {
		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);
			"Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
			func->name ? func->name : "", ret);
	}

	return ret;
}

	func->func_wakeup_pending = false;
	return 0;
}

static u8 encode_bMaxPower(enum usb_device_speed speed,
		struct usb_configuration *c)
{
@@ -677,6 +699,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);
	}
@@ -1882,14 +1905,22 @@ 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)
+15 −7
Original line number Diff line number Diff line
@@ -1525,15 +1525,23 @@ static int bam_data_wake_cb(void *param)
	if ((gadget->speed == USB_SPEED_SUPER) && (func->func_is_suspended)) {
		if (!func->func_wakeup_allowed)
			return -ENOTSUPP;
		else
			usb_func_wakeup(func);
		else {
			ret = usb_func_wakeup(func);
			if (ret)
				pr_err("Function wakeup failed. ret=%d\n", ret);
		}

	} else {
		ret = usb_gadget_wakeup(gadget);
		if (ret) {
		pr_err("Failed to wake up the USB core. ret=%d", ret);
			if ((ret == -EBUSY) || (ret == -EAGAIN))
				pr_debug("Remote wakeup is delayed due to low-power mode exit.\n");
			else
				pr_err("Failed to wake up the USB core. ret=%d.\n",
					ret);

			return ret;
		}
	}

	return 0;
}