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

Commit 34e3eb55 authored by Manu Gautam's avatar Manu Gautam
Browse files

usb: gadget: f_mbim: Issue wakeup if notification already queued



Selective suspend of device (with remote-wakeup enabled) by host
could race with device driver queuing a notification request on
interrupt endpoint. Driver currently issues remote wakeup if at
the time of queueing notification request device was suspended.
If host suspends device right after driver queues notification
request then device driver fails to initiate remote-wakeup.
Fix this by initiating remote wakeup if there were any pending
notification at the time of suspend.

Change-Id: I0402ad44941b87987381fa27009bdffd519b7e01
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
parent 3b8635c8
Loading
Loading
Loading
Loading
+38 −1
Original line number Diff line number Diff line
/* Copyright (c) 2012-2016,2017 The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -132,7 +132,9 @@ struct f_mbim {

	atomic_t		error;
	unsigned int		cpkt_drop_cnt;

	bool			remote_wakeup_enabled;
	struct delayed_work	rwake_work;
};

struct mbim_ntb_input_size {
@@ -667,6 +669,29 @@ static void mbim_clear_queues(struct f_mbim *mbim)
	spin_unlock(&mbim->lock);
}

static void mbim_remote_wakeup_work(struct work_struct *w)
{
	struct f_mbim	*mbim = container_of(w, struct f_mbim, rwake_work.work);
	int		ret = 0;

	if ((mbim->cdev->gadget->speed == USB_SPEED_SUPER) &&
			!mbim->function.func_is_suspended){
		pr_debug("%s: resume in progress\n", __func__);
		return;
	}

	if ((mbim->cdev->gadget->speed == USB_SPEED_SUPER) &&
			mbim->function.func_is_suspended)
		ret = usb_func_wakeup(&mbim->function);
	else
		ret = usb_gadget_wakeup(mbim->cdev->gadget);

	if (ret == -EBUSY || ret == -EAGAIN)
		pr_debug("%s:RW delayed due to LPM exit %d\n",  __func__, ret);
	else
		pr_info("%s: remote wake-up failed: %d\n", __func__, ret);
}

/*
 * Context: mbim->lock held
 */
@@ -1285,6 +1310,8 @@ static void mbim_disable(struct usb_function *f)
	struct usb_composite_dev *cdev = mbim->cdev;

	pr_info("SET DEVICE OFFLINE\n");

	cancel_delayed_work(&mbim->rwake_work);
	atomic_set(&mbim->online, 0);
	mbim->remote_wakeup_enabled = 0;

@@ -1362,6 +1389,13 @@ static void mbim_suspend(struct usb_function *f)

	bam_data_suspend(&mbim->bam_port, mbim->port_num, USB_FUNC_MBIM,
			 mbim->remote_wakeup_enabled);

	if (mbim->remote_wakeup_enabled &&
			atomic_read(&mbim->not_port.notify_count) > 0) {
		pr_info("%s: pending notification, wakeup host\n", __func__);
		schedule_delayed_work(&mbim->rwake_work,
				      msecs_to_jiffies(2000));
	}
}

static void mbim_resume(struct usb_function *f)
@@ -1381,6 +1415,8 @@ static void mbim_resume(struct usb_function *f)
		f->func_is_suspended)
		return;

	cancel_delayed_work(&mbim->rwake_work);

	/* resume control path by queuing notify req */
	spin_lock(&mbim->lock);
	mbim_do_notify(mbim);
@@ -1748,6 +1784,7 @@ int mbim_bind_config(struct usb_configuration *c, unsigned portno,

	INIT_LIST_HEAD(&mbim->cpkt_req_q);
	INIT_LIST_HEAD(&mbim->cpkt_resp_q);
	INIT_DELAYED_WORK(&mbim->rwake_work, mbim_remote_wakeup_work);

	status = usb_add_function(c, &mbim->function);