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

Commit 93379e36 authored by Manu Gautam's avatar Manu Gautam
Browse files

usb: f_rmnet: Add function suspend/resume for super-speed wakeup



USB super-speed mode mode requires device to advertise remote
wakeup capability at function driver level called as function
wakeup capability. Host queries this using get_status and
uses set_feature to enable function remote wakeup before
initiating function suspend. This is also required to initiate
remote wakeup as well which is otherwise failed by UDC in SS mode.

CRs-fixed: 2027141
Change-Id: I337de8b66299db08d4af967b858677f3b7c2a04d
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
parent 3ec9ec32
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -596,6 +596,12 @@ static void frmnet_suspend(struct usb_function *f)
	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
	bool			remote_wakeup_allowed;

	/* Check if function is already suspended in frmnet_func_suspend() */
	if (f->func_is_suspended) {
		pr_debug("%s: func already suspended!\n", __func__);
		return;
	}

	if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
		remote_wakeup_allowed = f->func_wakeup_allowed;
	else
@@ -657,6 +663,14 @@ static void frmnet_resume(struct usb_function *f)
	int  ret;
	bool remote_wakeup_allowed;

	/*
	 * If the function is in USB3 Function Suspend state, resume is
	 * canceled. In this case resume is done by a Function Resume request.
	 */
	if ((f->config->cdev->gadget->speed == USB_SPEED_SUPER) &&
		f->func_is_suspended)
		return;

	if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
		remote_wakeup_allowed = f->func_wakeup_allowed;
	else
@@ -698,6 +712,40 @@ static void frmnet_resume(struct usb_function *f)
	}
}

static int frmnet_func_suspend(struct usb_function *f, u8 options)
{
	bool func_wakeup_allowed;

	pr_debug("func susp %u cmd for %s", options, f->name ? f->name : "");

	func_wakeup_allowed =
		((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0);

	if (options & FUNC_SUSPEND_OPT_SUSP_MASK) {
		f->func_wakeup_allowed = func_wakeup_allowed;
		if (!f->func_is_suspended) {
			frmnet_suspend(f);
			f->func_is_suspended = true;
		}
	} else {
		if (f->func_is_suspended) {
			f->func_is_suspended = false;
			frmnet_resume(f);
		}
		f->func_wakeup_allowed = func_wakeup_allowed;
	}

	return 0;
}

static int frmnet_get_status(struct usb_function *f)
{
	unsigned remote_wakeup_en_status = f->func_wakeup_allowed ? 1 : 0;

	return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) |
		(1 << FUNC_WAKEUP_CAPABLE_SHIFT);
}

static void frmnet_disable(struct usb_function *f)
{
	struct f_rmnet *dev = func_to_rmnet(f);
@@ -1295,6 +1343,7 @@ static int frmnet_bind_config(struct usb_configuration *c, unsigned portno)
	spin_lock_irqsave(&dev->lock, flags);
	dev->cdev = c->cdev;
	f = &dev->gether_port.func;
	dev->port.f = f;
	f->name = kasprintf(GFP_ATOMIC, "rmnet%d", portno);
	spin_unlock_irqrestore(&dev->lock, flags);
	if (!f->name) {
@@ -1310,6 +1359,8 @@ static int frmnet_bind_config(struct usb_configuration *c, unsigned portno)
	f->setup = frmnet_setup;
	f->suspend = frmnet_suspend;
	f->resume = frmnet_resume;
	f->func_suspend = frmnet_func_suspend;
	f->get_status = frmnet_get_status;
	dev->port.send_cpkt_response = frmnet_send_cpkt_response;
	dev->port.disconnect = frmnet_disconnect;
	dev->port.connect = frmnet_connect;
+54 −1
Original line number Diff line number Diff line
@@ -1571,6 +1571,8 @@ static int gbam_wake_cb(void *param)
	struct gbam_port	*port = (struct gbam_port *)param;
	struct usb_gadget	*gadget;
	unsigned long flags;
	struct usb_function *func;
	int ret;

	spin_lock_irqsave(&port->port_lock, flags);
	if (!port->port_usb) {
@@ -1581,11 +1583,23 @@ static int gbam_wake_cb(void *param)
	}

	gadget = port->port_usb->gadget;
	func = port->port_usb->f;
	spin_unlock_irqrestore(&port->port_lock, flags);

	pr_debug("%s: woken up by peer\n", __func__);

	return usb_gadget_wakeup(gadget);
	if ((gadget->speed == USB_SPEED_SUPER) &&
	    (func->func_is_suspended))
		ret = usb_func_wakeup(func);
	else
		ret = usb_gadget_wakeup(gadget);

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

	return ret;
}

static void gbam2bam_suspend_work(struct work_struct *w)
@@ -2014,6 +2028,42 @@ const struct file_operations gbam_stats_ops = {
	.write = gbam_reset_stats,
};

static ssize_t gbam_rw_write(struct file *file, const char __user *ubuf,
				size_t count, loff_t *ppos)
{
	struct gbam_port	*port = bam2bam_ports[0];
	struct usb_function	*func;
	struct usb_gadget	*gadget;
	unsigned long		flags;

	if (!port)
		return -ENODEV;

	spin_lock_irqsave(&port->port_lock, flags);
	if (!port->port_usb) {
		pr_debug("%s: usb cable is disconnected, exiting\n",
				__func__);
		spin_unlock_irqrestore(&port->port_lock, flags);
		return -ENODEV;
	}

	gadget = port->port_usb->gadget;
	func = port->port_usb->f;
	spin_unlock_irqrestore(&port->port_lock, flags);

	if ((gadget->speed == USB_SPEED_SUPER) && (func->func_is_suspended)) {
		pr_debug("%s Initiating usb_func rwakeup\n", __func__);
		usb_func_wakeup(func);
	}

	return count;
}


const struct file_operations debug_remote_wakeup_fops = {
	.write = gbam_rw_write,
};

struct dentry *gbam_dent;
static void gbam_debugfs_init(void)
{
@@ -2026,6 +2076,9 @@ static void gbam_debugfs_init(void)
	if (!gbam_dent || IS_ERR(gbam_dent))
		return;

	debugfs_create_file("remote_wakeup", 0444, gbam_dent, 0,
			&debug_remote_wakeup_fops);

	dfile = debugfs_create_file("status", 0444, gbam_dent, 0,
			&gbam_stats_ops);
	if (!dfile || IS_ERR(dfile)) {
+2 −1
Original line number Diff line number Diff line
@@ -25,7 +25,8 @@ struct rmnet_ctrl_pkt {
};

struct grmnet {
	struct usb_function		func;
	struct usb_function		func; /* Used only by f_gps */
	struct usb_function		*f;

	struct usb_gadget		*gadget;
	struct usb_ep			*in;