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

Commit 9bad43b6 authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: gadget: add infrastructure support for super-speed function suspend" into msm-4.8

parents ec74f684 f83e4511
Loading
Loading
Loading
Loading
+119 −2
Original line number Diff line number Diff line
@@ -418,6 +418,114 @@ 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;
}

int usb_func_wakeup(struct usb_function *func)
{
	int ret;
	unsigned int interface_id;
	struct usb_gadget *gadget;

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

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

	gadget = func->config->cdev->gadget;
	if ((gadget->speed != USB_SPEED_SUPER) || !func->func_wakeup_allowed) {
		DBG(func->config->cdev,
			"Function Wakeup is not possible. speed=%u, func_wakeup_allowed=%u\n",
			gadget->speed,
			func->func_wakeup_allowed);

		return -ENOTSUPP;
	}

	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);
		return ret;
	}

	interface_id = ret;
	ret = usb_gadget_func_wakeup(gadget, interface_id);
	if (ret) {
		if (ret == -EAGAIN) {
			DBG(func->config->cdev,
				"Function wakeup for %s could not be complete. Retry is needed.\n",
				func->name ? func->name : "");
		} else {
			ERROR(func->config->cdev,
				"Failed to wake function %s from suspend state. interface id: %d, ret=%d. Canceling USB request.\n",
				func->name ? func->name : "",
				interface_id, ret);
		}
		return ret;
	}

	return 0;
}
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;
	struct usb_gadget *gadget;

	if (!func || !ep || !req) {
		pr_err("Invalid argument. func=%p, ep=%p, req=%p\n",
			func, 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) {
			if (ret != -EAGAIN)
				pr_err("Failed to send function wake up notification. func name:%s, ep:%u\n",
					func->name ? func->name : "",
					ep->address);
			return ret;
		}
	}

	ret = usb_ep_queue(ep, req, gfp_flags);
	return ret;
}
EXPORT_SYMBOL(usb_func_ep_queue);

static u8 encode_bMaxPower(enum usb_device_speed speed,
		struct usb_configuration *c)
{
@@ -730,6 +838,10 @@ static void reset_config(struct usb_composite_dev *cdev)
		if (f->disable)
			f->disable(f);

		/* USB 3.0 addition */
		f->func_is_suspended = false;
		f->func_wakeup_allowed = false;

		bitmap_zero(f->endpoints, 32);
	}
	cdev->config = NULL;
@@ -1780,8 +1892,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
			if (!f)
				break;
			value = 0;
			if (f->func_suspend)
				value = f->func_suspend(f, w_index >> 8);
			if (f->func_suspend) {
				const u8 suspend_opt = w_index >> 8;

				value = f->func_suspend(f, suspend_opt);
				DBG(cdev, "%s function: FUNCTION_SUSPEND(%u)",
					f->name ? f->name : "", suspend_opt);
			}
			if (value < 0) {
				ERROR(cdev,
				      "func_suspend() returned error %d\n",
+10 −1
Original line number Diff line number Diff line
@@ -158,7 +158,11 @@ struct usb_os_desc_table {
 * @get_status: Returns function status as a reply to
 *	GetStatus() request when the recipient is Interface.
 * @func_suspend: callback to be called when
 *	SetFeature(FUNCTION_SUSPEND) is reseived
 *	SetFeature(FUNCTION_SUSPEND) is received
 * @func_is_suspended: Tells whether the function is currently in
 *	Function Suspend state (used in Super Speed mode only).
 * @func_wakeup_allowed: Tells whether Function Remote Wakeup has been allowed
 *	by the USB host (used in Super Speed mode only).
 *
 * A single USB function uses one or more interfaces, and should in most
 * cases support operation at both full and high speeds.  Each function is
@@ -229,6 +233,8 @@ struct usb_function {
	int			(*get_status)(struct usb_function *);
	int			(*func_suspend)(struct usb_function *,
						u8 suspend_opt);
	unsigned		func_is_suspended:1;
	unsigned		func_wakeup_allowed:1;
	/* private: */
	/* internals */
	struct list_head		list;
@@ -244,6 +250,9 @@ int usb_function_deactivate(struct usb_function *);
int usb_function_activate(struct usb_function *);

int usb_interface_id(struct usb_configuration *, struct usb_function *);
int usb_func_wakeup(struct usb_function *func);

int usb_get_func_interface_id(struct usb_function *func);

int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
			struct usb_ep *_ep);
+19 −1
Original line number Diff line number Diff line
@@ -402,6 +402,7 @@ struct usb_udc;
struct usb_gadget_ops {
	int	(*get_frame)(struct usb_gadget *);
	int	(*wakeup)(struct usb_gadget *);
	int	(*func_wakeup)(struct usb_gadget *, int interface_id);
	int	(*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
	int	(*vbus_session) (struct usb_gadget *, int is_active);
	int	(*vbus_draw) (struct usb_gadget *, unsigned mA);
@@ -415,7 +416,6 @@ struct usb_gadget_ops {
	struct usb_ep *(*match_ep)(struct usb_gadget *,
			struct usb_endpoint_descriptor *,
			struct usb_ss_ep_comp_descriptor *);
	int	(*func_wakeup)(struct usb_gadget *, int interface_id);
	int	(*restart)(struct usb_gadget *);
};

@@ -899,6 +899,24 @@ int usb_otg_descriptor_init(struct usb_gadget *gadget,
		struct usb_descriptor_header *otg_desc);
/*-------------------------------------------------------------------------*/

/**
 * usb_func_ep_queue - queues (submits) an I/O request to a function endpoint.
 * This function is similar to the usb_ep_queue function, but in addition it
 * also checks whether the function is in Super Speed USB Function Suspend
 * state, and if so a Function Wake notification is sent to the host
 * (USB 3.0 spec, section 9.2.5.2).
 * @func: the function which issues the USB I/O request.
 * @ep:the endpoint associated with the request
 * @req:the request being submitted
 * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
 *	pre-allocate all necessary memory with the request.
 *
 */
int usb_func_ep_queue(struct usb_function *func, struct usb_ep *ep,
				struct usb_request *req, gfp_t gfp_flags);

/*-------------------------------------------------------------------------*/

/* utility to simplify map/unmap of usb_requests to/from DMA */

extern int usb_gadget_map_request_by_dev(struct device *dev,