From 1e6f91ba622a7ad1a6af68af46d409588a9db94c Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 29 Jan 2021 12:53:49 -0800 Subject: [PATCH] usb: f_cdev: Retry USB function wakeup if USB is into low power mode Consider case where USB is into low power mode after receiving USB function suspend from host. To invoke function remote wakeup USB controller is rerquired to bring out of low power mode. Hence once USB controller and USB bus is resumed, perform USB function wakeup from composite_resume() context to resume USB function on host side. Change-Id: If15b70bc7d3781bc26bf593e3ee3edef621f72d7 Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_cdev.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c index 767b107216a0..2b52f86a6072 100644 --- a/drivers/usb/gadget/function/f_cdev.c +++ b/drivers/usb/gadget/function/f_cdev.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2011, 2013-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2011, 2013-2021, The Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 license terms, * and distributes only under these terms. * @@ -110,6 +110,7 @@ struct f_cdev { /* function suspend status */ bool func_is_suspended; bool func_wakeup_allowed; + bool func_wakeup_pending; struct cserial port_usb; @@ -530,10 +531,26 @@ static int usb_cser_set_alt(struct usb_function *f, unsigned int intf, } } + port->func_wakeup_pending = false; usb_cser_connect(port); return rc; } +static void usb_cser_resume(struct usb_function *f) +{ + struct f_cdev *port = func_to_port(f); + struct usb_composite_dev *cdev = f->config->cdev; + + if (cdev->gadget->speed >= USB_SPEED_SUPER && port->func_is_suspended) { + if (port->func_wakeup_pending) { + dev_dbg(&cdev->gadget->dev, + "func_wakeup for port:%s\n", port->name); + usb_func_wakeup(&port->port_usb.func); + port->func_wakeup_pending = false; + } + } +} + static int usb_cser_func_suspend(struct usb_function *f, u8 options) { struct f_cdev *port = func_to_port(f); @@ -566,6 +583,7 @@ static void usb_cser_disable(struct usb_function *f) "port(%s) deactivated\n", port->name); usb_cser_disconnect(port); + port->func_wakeup_pending = false; usb_ep_disable(port->port_usb.notify); port->port_usb.notify->driver_data = NULL; } @@ -1653,6 +1671,8 @@ static ssize_t cser_rw_write(struct file *file, const char __user *ubuf, port->func_is_suspended) { pr_debug("Calling usb_func_wakeup\n"); ret = usb_func_wakeup(func); + if (ret == -EAGAIN) + port->func_wakeup_pending = true; } else { pr_debug("Calling usb_gadget_wakeup\n"); ret = usb_gadget_wakeup(gadget); @@ -2047,6 +2067,7 @@ static struct usb_function *cser_alloc(struct usb_function_instance *fi) port->port_usb.func.disable = usb_cser_disable; port->port_usb.func.setup = usb_cser_setup; port->port_usb.func.func_suspend = usb_cser_func_suspend; + port->port_usb.func.resume = usb_cser_resume; port->port_usb.func.get_status = usb_cser_get_status; port->port_usb.func.free_func = usb_cser_free_func;