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

Commit 3ee13ad1 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: gadget: f_cdev: Fix func_suspend"

parents 6b47c82f 84caa0c7
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -595,6 +595,17 @@ config USB_CONFIGFS_F_GSI
	  related functionalities using GSI hardware accelerated data
	  path and control path.

config USB_FUNC_WAKEUP_SUPPORTED
	bool "USB Function Remote Wakeup support"
	depends on QGKI
	help
	  USB 3.x allows functions to be independently suspended for better
	  power management on a per-interface basis. Additionally a function
	  can also be a remote wake source. Enable this option to add
	  support for allowing a function to issue a remote wakeup to the
	  composite driver, which in turn will issue a device notification
	  packet request to the device controller driver.

choice
	tristate "USB Gadget precomposed configurations"
	default USB_ETH
+45 −0
Original line number Diff line number Diff line
@@ -426,6 +426,51 @@ int usb_interface_id(struct usb_configuration *config,
}
EXPORT_SYMBOL_GPL(usb_interface_id);

#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
int usb_func_wakeup(struct usb_function *func)
{
	int ret, id;
	unsigned long flags;

	if (!func || !func->config || !func->config->cdev ||
		!func->config->cdev->gadget)
		return -EINVAL;

	DBG(func->config->cdev, "%s function wakeup\n", func->name);

	spin_lock_irqsave(&func->config->cdev->lock, flags);

	for (id = 0; id < MAX_CONFIG_INTERFACES; id++)
		if (func->config->interface[id] == func)
			break;

	if (id == MAX_CONFIG_INTERFACES) {
		ERROR(func->config->cdev, "Invalid function id:%d\n", id);
		ret = -EINVAL;
		goto err;
	}

	ret = usb_gadget_func_wakeup(func->config->cdev->gadget, id);

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

err:
	spin_unlock_irqrestore(&func->config->cdev->lock, flags);

	return ret;
}
EXPORT_SYMBOL(usb_func_wakeup);
#endif

static u8 encode_bMaxPower(enum usb_device_speed speed,
		struct usb_configuration *c)
{
+18 −17
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2011, 2013-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2011, 2013-2020, The Linux Foundation. All rights reserved.
 * Linux Foundation chooses to take subject only to the GPLv2 license terms,
 * and distributes only under these terms.
 *
@@ -107,6 +107,10 @@ struct f_cdev {
	/* current USB RX buffer */
	u8			*current_rx_buf;

	/* function suspend status */
	bool			func_is_suspended;
	bool			func_wakeup_allowed;

	struct cserial		port_usb;

#define ACM_CTRL_DTR		0x01
@@ -514,28 +518,25 @@ static int usb_cser_set_alt(struct usb_function *f, unsigned int intf,

static int usb_cser_func_suspend(struct usb_function *f, u8 options)
{
	bool func_wakeup_allowed;
	struct f_cdev	*port = func_to_port(f);

	func_wakeup_allowed =
		((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0);
	port->func_wakeup_allowed =
		!!(options & (USB_INTRF_FUNC_SUSPEND_RW >> 8));
	port->func_is_suspended = options & (USB_INTRF_FUNC_SUSPEND_LP >> 8);

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

static int usb_cser_get_status(struct usb_function *f)
{
	bool remote_wakeup_en_status = f->func_wakeup_allowed ? 1 : 0;
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
	struct f_cdev	*port = func_to_port(f);

	return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) |
		(1 << FUNC_WAKEUP_CAPABLE_SHIFT);
	return (port->func_wakeup_allowed ? USB_INTRF_STAT_FUNC_RW : 0) |
		USB_INTRF_STAT_FUNC_RW_CAP;
#else
	return 0;
#endif
}

static void usb_cser_disable(struct usb_function *f)
@@ -1584,8 +1585,8 @@ static ssize_t cser_rw_write(struct file *file, const char __user *ubuf,
	port->debugfs_rw_enable = !!input;
	if (port->debugfs_rw_enable) {
		gadget = cser->func.config->cdev->gadget;
		if (gadget->speed == USB_SPEED_SUPER &&
			func->func_is_suspended) {
		if (gadget->speed >= USB_SPEED_SUPER &&
			port->func_is_suspended) {
			pr_debug("Calling usb_func_wakeup\n");
			ret = usb_func_wakeup(func);
		} else {
+34 −26
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
 */

#include <linux/module.h>
@@ -34,13 +34,11 @@ static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt);

static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
{
	bool remote_wakeup_allowed;
	bool remote_wakeup_allowed = true;
	struct f_gsi *gsi = func_to_gsi(f);

	if (f->config->cdev->gadget->speed >= USB_SPEED_SUPER)
		remote_wakeup_allowed = f->func_wakeup_allowed;
	else
		remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
		remote_wakeup_allowed = gsi->func_wakeup_allowed;

	log_event_dbg("%s: remote_wakeup_allowed:%s", __func__,
			(remote_wakeup_allowed ? "true" : "false"));
@@ -156,7 +154,7 @@ static int gsi_wakeup_host(struct f_gsi *gsi)
	 * allowed to do so by the host. This is done in order to support non
	 * fully USB 3.0 compatible hosts.
	 */
	if ((gadget->speed >= USB_SPEED_SUPER) && (func->func_is_suspended)) {
	if ((gadget->speed >= USB_SPEED_SUPER) && (gsi->func_is_suspended)) {
		log_event_dbg("%s: Calling usb_func_wakeup", __func__);
		ret = usb_func_wakeup(func);
	} else {
@@ -730,7 +728,7 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
	struct f_gsi *gsi = d_port_to_gsi(d_port);
	struct usb_function *f = &gsi->function;

	f_suspend = f->func_wakeup_allowed;
	f_suspend = gsi->func_wakeup_allowed;
	log_event_dbg("%s: f_suspend:%d", __func__, f_suspend);

	if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend,
@@ -1377,8 +1375,7 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf,
		return -ECONNRESET;
	}

	if (gsi->function.func_is_suspended &&
			!gsi->function.func_wakeup_allowed) {
	if (gsi->func_is_suspended && !gsi->func_wakeup_allowed) {
		c_port->cpkt_drop_cnt++;
		log_event_err("drop ctrl pkt of len %zu", count);
		return -ENOTSUPP;
@@ -1792,9 +1789,17 @@ static int queue_notification_request(struct f_gsi *gsi)
	int ret;
	unsigned long flags;

	ret = usb_func_ep_queue(&gsi->function, gsi->c_port.notify,
	if (!gsi->func_is_suspended) {
		ret = usb_ep_queue(gsi->c_port.notify,
				   gsi->c_port.notify_req, GFP_ATOMIC);
	if (ret < 0) {
	} else {
		if (gsi->func_wakeup_allowed)
			ret = usb_func_wakeup(&gsi->function);
		else
			ret = -EOPNOTSUPP;
	}

	if (ret < 0 || gsi->func_is_suspended) {
		spin_lock_irqsave(&gsi->c_port.lock, flags);
		gsi->c_port.notify_req_queued = false;
		spin_unlock_irqrestore(&gsi->c_port.lock, flags);
@@ -2435,7 +2440,7 @@ static void gsi_suspend(struct usb_function *f)
	struct f_gsi *gsi = func_to_gsi(f);

	/* Check if function is already suspended in gsi_func_suspend() */
	if (f->func_is_suspended) {
	if (gsi->func_is_suspended) {
		log_event_dbg("%s: func already suspended, return\n", __func__);
		return;
	}
@@ -2460,7 +2465,7 @@ static void gsi_resume(struct usb_function *f)
	 * canceled. In this case resume is done by a Function Resume request.
	 */
	if ((cdev->gadget->speed >= USB_SPEED_SUPER) &&
		f->func_is_suspended)
		gsi->func_is_suspended)
		return;

	if (gsi->c_port.notify && !gsi->c_port.notify->desc)
@@ -2488,10 +2493,14 @@ static void gsi_resume(struct usb_function *f)

static int gsi_get_status(struct usb_function *f)
{
	unsigned int remote_wakeup_en_status = f->func_wakeup_allowed ? 1 : 0;
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
	struct f_gsi *gsi = func_to_gsi(f);

	return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) |
		(1 << FUNC_WAKEUP_CAPABLE_SHIFT);
	return (gsi->func_wakeup_allowed ? USB_INTRF_STAT_FUNC_RW : 0) |
		USB_INTRF_STAT_FUNC_RW_CAP;
#else
	return 0;
#endif
}

static int gsi_func_suspend(struct usb_function *f, u8 options)
@@ -2502,21 +2511,20 @@ static int gsi_func_suspend(struct usb_function *f, u8 options)
	log_event_dbg("func susp %u cmd for %s",
		options, f->name ? f->name : "");

	func_wakeup_allowed =
		((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0);
	func_wakeup_allowed = !!(options & (USB_INTRF_FUNC_SUSPEND_RW >> 8));

	if (options & FUNC_SUSPEND_OPT_SUSP_MASK) {
		f->func_wakeup_allowed = func_wakeup_allowed;
		if (!f->func_is_suspended) {
	if (options & (USB_INTRF_FUNC_SUSPEND_LP >> 8)) {
		gsi->func_wakeup_allowed = func_wakeup_allowed;
		if (!gsi->func_is_suspended) {
			gsi_suspend(f);
			f->func_is_suspended = true;
			gsi->func_is_suspended = true;
		}
	} else {
		if (f->func_is_suspended) {
			f->func_is_suspended = false;
		if (gsi->func_is_suspended) {
			gsi->func_is_suspended = false;
			gsi_resume(f);
		}
		f->func_wakeup_allowed = func_wakeup_allowed;
		gsi->func_wakeup_allowed = func_wakeup_allowed;
	}

	return 0;
+5 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
 */

#ifndef _F_GSI_H
@@ -269,6 +269,10 @@ struct f_gsi {
	bool data_interface_up;
	enum rndis_class_id rndis_id;

	/* function suspend status */
	bool func_is_suspended;
	bool func_wakeup_allowed;

	const struct usb_endpoint_descriptor *in_ep_desc_backup;
	const struct usb_endpoint_descriptor *out_ep_desc_backup;

Loading