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

Commit 2f2ed9a8 authored by Hemant Kumar's avatar Hemant Kumar Committed by Mayank Rana
Browse files

usb: gadget: f_mbim: Queue notification request upon function resume



In super speed mode if userspace issues a write after usb bus suspend
usb_func_ep_queue() schedules wakeup to resume the function. After that
it queues the request which fails with -ENOTSUPP. As a result no
notification request queued to hw and write request gets delayed to be
sent until another write request comes and queues notification request
after function resume. This causes mismatch to the mbim request response.
Fix this by queuing the notification request upon function resume if
notify count is greater than zero. Also, drop control packet after bus
suspend if remote wakeup is not supported or if ep enqueue returns error
other than -EAGAIN.

CRs-Fixed: 789467
Change-Id: I446de1eb169b4ccb8f4db5f003b622d7b9c0b22b
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
Signed-off-by: default avatarAzhar Shaikh <azhars@codeaurora.org>
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent b92dfd09
Loading
Loading
Loading
Loading
+20 −54
Original line number Diff line number Diff line
@@ -411,6 +411,7 @@ int usb_interface_id(struct usb_configuration *config,

	if (id < MAX_CONFIG_INTERFACES) {
		config->interface[id] = function;
		function->intf_id = id;
		config->next_interface_id = id + 1;
		return id;
	}
@@ -418,47 +419,20 @@ int usb_interface_id(struct usb_configuration *config,
}
EXPORT_SYMBOL_GPL(usb_interface_id);

/**
 * usb_get_func_interface_id() - Find the interface ID of a function
 * @function: the function for which want to find the interface ID
 * Context: single threaded
 *
 * Returns the interface ID of the function or -ENODEV if this function
 * is not part of this configuration
 */
int usb_get_func_interface_id(struct usb_function *func)
{
	int id;
	struct usb_configuration *config;

	if (!func)
		return -EINVAL;

	config = func->config;

	for (id = 0; id < MAX_CONFIG_INTERFACES; id++) {
		if (config->interface[id] == func)
			return id;
	}
	return -ENODEV;
}

static int usb_func_wakeup_int(struct usb_function *func)
{
	int ret;
	int interface_id;
	unsigned long flags;
	struct usb_gadget *gadget;
	struct usb_composite_dev *cdev;

	pr_debug("%s - %s function wakeup\n",
		__func__, func->name ? func->name : "");

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

	pr_debug("%s - %s function wakeup\n", __func__,
					func->name ? func->name : "");

	gadget = func->config->cdev->gadget;
	if ((gadget->speed != USB_SPEED_SUPER) || !func->func_wakeup_allowed) {
		DBG(func->config->cdev,
@@ -471,18 +445,7 @@ static int usb_func_wakeup_int(struct usb_function *func)

	cdev = get_gadget_data(gadget);
	spin_lock_irqsave(&cdev->lock, flags);
	ret = usb_get_func_interface_id(func);
	if (ret < 0) {
		ERROR(func->config->cdev,
			"Function %s - Unknown interface id. Canceling USB request. ret=%d\n",
			func->name ? func->name : "", ret);

		spin_unlock_irqrestore(&cdev->lock, flags);
		return ret;
	}

	interface_id = ret;
	ret = usb_gadget_func_wakeup(gadget, interface_id);
	ret = usb_gadget_func_wakeup(gadget, func->intf_id);
	spin_unlock_irqrestore(&cdev->lock, flags);

	return ret;
@@ -514,30 +477,33 @@ EXPORT_SYMBOL(usb_func_wakeup);
int usb_func_ep_queue(struct usb_function *func, struct usb_ep *ep,
			       struct usb_request *req, gfp_t gfp_flags)
{
	int ret;
	int ret = -ENOTSUPP;
	struct usb_gadget *gadget;

	if (!func || !ep || !req) {
		pr_err("Invalid argument. func=%p, ep=%p, req=%p\n",
			func, ep, req);
	if (!func || !func->config || !func->config->cdev ||
			!func->config->cdev->gadget || !ep || !req)
		return -EINVAL;
	}

	pr_debug("Function %s queueing new data into ep %u\n",
		func->name ? func->name : "", ep->address);

	gadget = func->config->cdev->gadget;
	if ((gadget->speed == USB_SPEED_SUPER) && func->func_is_suspended) {
		ret = usb_func_wakeup(func);
		if (ret) {
			pr_err("Failed to send function wake up notification. func name:%s, ep:%u\n",
				func->name ? func->name : "",
				ep->address);
			return ret;
	if (func->func_is_suspended && func->func_wakeup_allowed) {
		ret = usb_gadget_func_wakeup(gadget, func->intf_id);
		if (ret == -EAGAIN) {
			pr_debug("bus suspended func wakeup for %s delayed until bus resume.\n",
				func->name ? func->name : "");
		} else if (ret < 0 && ret != -ENOTSUPP) {
			pr_err("Failed to wake function %s from suspend state. ret=%d.\n",
				func->name ? func->name : "", ret);
		}
	}

	if (!func->func_is_suspended)
		ret = 0;
	if (!ret)
		ret = usb_ep_queue(ep, req, gfp_flags);

	return ret;
}
EXPORT_SYMBOL(usb_func_ep_queue);
+2 −0
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ struct usb_os_desc_table {
/**
 * struct usb_function - describes one function of a configuration
 * @name: For diagnostics, identifies the function.
 * @intf_id: Interface ID
 * @strings: tables of strings, keyed by identifiers assigned during bind()
 *	and by language IDs provided in control requests
 * @fs_descriptors: Table of full (or low) speed descriptors, using interface and
@@ -197,6 +198,7 @@ struct usb_os_desc_table {

struct usb_function {
	const char			*name;
	int				intf_id;
	struct usb_gadget_strings	**strings;
	struct usb_descriptor_header	**fs_descriptors;
	struct usb_descriptor_header	**hs_descriptors;