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

Commit 3d746d25 authored by Manu Gautam's avatar Manu Gautam
Browse files

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



USB super-speed 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 required to initiate
remote wakeup as well which is failed by udc otherwise in SS mode.

CRs-Fixed: 964728
Change-Id: I6bb87b334ca1a5363c9d8e607c7a3d07deab04f2
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
parent 747b0f6a
Loading
Loading
Loading
Loading
+45 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@
 * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
 * Copyright (C) 2008 by David Brownell
 * Copyright (C) 2008 by Nokia Corporation
 * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
 * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
 *
 * This software is distributed under the terms of the GNU General
 * Public License ("GPL") as published by the Free Software Foundation,
@@ -643,6 +643,7 @@ static void gser_suspend(struct usb_function *f)
static void gser_resume(struct usb_function *f)
{
	struct f_gser	*gser = func_to_gser(f);
	struct usb_composite_dev *cdev = gser->port.func.config->cdev;
	unsigned port_num;

	port_num = gserial_ports[gser->port_num].client_port_num;
@@ -650,6 +651,13 @@ static void gser_resume(struct usb_function *f)
	pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
			__func__, xport_to_str(gser->transport),
			gser, &gser->port, gser->port_num);
	/*
	 * If the function is in USB3 Function Suspend state, resume is
	 * canceled. In this case resume is done by a Function Resume request.
	 */
	if ((cdev->gadget->speed == USB_SPEED_SUPER) &&
		f->func_is_suspended)
		return;

	switch (gser->transport) {
	case USB_GADGET_XPORT_SMD:
@@ -661,6 +669,40 @@ static void gser_resume(struct usb_function *f)
	}
}

static int gser_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) {
			gser_suspend(f);
			f->func_is_suspended = true;
		}
	} else {
		if (f->func_is_suspended) {
			f->func_is_suspended = false;
			gser_resume(f);
		}
		f->func_wakeup_allowed = func_wakeup_allowed;
	}

	return 0;
}

static int gser_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 int gser_notify(struct f_gser *gser, u8 type, u16 value,
		void *data, unsigned length)
{
@@ -1100,6 +1142,8 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
	gser->port.func.setup = gser_setup;
	gser->port.func.suspend = gser_suspend;
	gser->port.func.resume = gser_resume;
	gser->port.func.func_suspend = gser_func_suspend;
	gser->port.func.get_status = gser_get_status;
	gser->port.connect = gser_connect;
	gser->port.get_dtr = gser_get_dtr;
	gser->port.get_rts = gser_get_rts;
+11 −6
Original line number Diff line number Diff line
/*
 * u_smd.c - utilities for USB gadget serial over smd
 *
 * Copyright (c) 2011, 2013-2015, The Linux Foundation. All rights reserved.
 * Copyright (c) 2011, 2013-2016, The Linux Foundation. All rights reserved.
 *
 * This code also borrows from drivers/usb/gadget/u_serial.c, which is
 * Copyright (C) 2000 - 2003 Al Borchers (alborchers@steinerpoint.com)
@@ -322,13 +322,18 @@ static void gsmd_tx_pull(struct work_struct *w)
	gadget = func->config->cdev->gadget;
	if (port->is_suspended) {
		spin_unlock_irq(&port->port_lock);
		if ((gadget->speed == USB_SPEED_SUPER) &&
		    (func->func_is_suspended))
			ret = usb_func_wakeup(func);
		else
			ret = usb_gadget_wakeup(gadget);
		spin_lock_irq(&port->port_lock);

		if (ret)
			pr_err("Failed to wake up the USB core. ret=%d.\n",
				ret);
		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);

		spin_lock_irq(&port->port_lock);
		if (!port->port_usb) {
			pr_debug("%s: USB disconnected\n", __func__);
			spin_unlock_irq(&port->port_lock);
+3 −0
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@
#define FUNC_SUSPEND_OPT_SUSP_MASK BIT(0)
#define FUNC_SUSPEND_OPT_RW_EN_MASK BIT(1)

#define FUNC_WAKEUP_CAPABLE_SHIFT  0
#define FUNC_WAKEUP_ENABLE_SHIFT   1

/*
 * USB function drivers should return USB_GADGET_DELAYED_STATUS if they
 * wish to delay the data/status stages of the control transfer till they