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

Commit bf76eb8e authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 4ea38935 44583bda
Loading
Loading
Loading
Loading
+80 −2
Original line number Original line Diff line number Diff line
@@ -338,6 +338,75 @@ int usb_interface_id(struct usb_configuration *config,
}
}
EXPORT_SYMBOL_GPL(usb_interface_id);
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 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",
			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",
			func->name ? func->name : "", ret);
		return ret;
	}

	interface_id = ret;
	ret = usb_gadget_func_wakeup(gadget, interface_id);
	if (ret) {
		ERROR(func->config->cdev,
			"Failed to wake function %s from suspend state. interface id: %d, ret=%d. Canceling USB request.",
			func->name ? func->name : "",
			interface_id, ret);
		return ret;
	}

	return 0;
}

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


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

		bitmap_zero(f->endpoints, 32);
		bitmap_zero(f->endpoints, 32);
	}
	}
	cdev->config = NULL;
	cdev->config = NULL;
@@ -1452,8 +1525,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
			if (!f)
			if (!f)
				break;
				break;
			value = 0;
			value = 0;
			if (f->func_suspend)
			if (f->func_suspend) {
				value = f->func_suspend(f, w_index >> 8);
				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) {
			if (value < 0) {
				ERROR(cdev,
				ERROR(cdev,
				      "func_suspend() returned error %d\n",
				      "func_suspend() returned error %d\n",
+31 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@


#include <linux/usb/ch9.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>


/**
/**
 * struct usb_udc - describes one usb device controller
 * struct usb_udc - describes one usb device controller
@@ -437,6 +438,36 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
}
}
EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);


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) {
			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;
}


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


static ssize_t usb_udc_srp_store(struct device *dev,
static ssize_t usb_udc_srp_store(struct device *dev,
+10 −1
Original line number Original line Diff line number Diff line
@@ -94,7 +94,11 @@ struct usb_configuration;
 * @get_status: Returns function status as a reply to
 * @get_status: Returns function status as a reply to
 *	GetStatus() request when the recepient is Interface.
 *	GetStatus() request when the recepient is Interface.
 * @func_suspend: callback to be called when
 * @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
 * 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
 * cases support operation at both full and high speeds.  Each function is
@@ -158,6 +162,8 @@ struct usb_function {
	int			(*get_status)(struct usb_function *);
	int			(*get_status)(struct usb_function *);
	int			(*func_suspend)(struct usb_function *,
	int			(*func_suspend)(struct usb_function *,
						u8 suspend_opt);
						u8 suspend_opt);
	unsigned		func_is_suspended:1;
	unsigned		func_wakeup_allowed:1;
	/* private: */
	/* private: */
	/* internals */
	/* internals */
	struct list_head		list;
	struct list_head		list;
@@ -171,6 +177,9 @@ int usb_function_deactivate(struct usb_function *);
int usb_function_activate(struct usb_function *);
int usb_function_activate(struct usb_function *);


int usb_interface_id(struct usb_configuration *, 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,
int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
			struct usb_ep *_ep);
			struct usb_ep *_ep);
+39 −0
Original line number Original line Diff line number Diff line
@@ -471,6 +471,7 @@ struct usb_gadget_driver;
struct usb_gadget_ops {
struct usb_gadget_ops {
	int	(*get_frame)(struct usb_gadget *);
	int	(*get_frame)(struct usb_gadget *);
	int	(*wakeup)(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	(*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
	int	(*vbus_session) (struct usb_gadget *, int is_active);
	int	(*vbus_session) (struct usb_gadget *, int is_active);
	int	(*vbus_draw) (struct usb_gadget *, unsigned mA);
	int	(*vbus_draw) (struct usb_gadget *, unsigned mA);
@@ -646,6 +647,26 @@ static inline int usb_gadget_wakeup(struct usb_gadget *gadget)
	return gadget->ops->wakeup(gadget);
	return gadget->ops->wakeup(gadget);
}
}


/**
 * usb_gadget_func_wakeup - send a function remote wakeup up notification
 * to the host connected to this gadget
 * @gadget: controller used to wake up the host
 * @interface_id: the interface which triggered the remote wakeup event
 *
 * Returns zero on success. Otherwise, negative error code is returned.
 */
static inline int usb_gadget_func_wakeup(struct usb_gadget *gadget,
	int interface_id)
{
	if (gadget->speed != USB_SPEED_SUPER)
		return -EOPNOTSUPP;

	if (!gadget->ops->func_wakeup)
		return -EOPNOTSUPP;

	return gadget->ops->func_wakeup(gadget, interface_id);
}

/**
/**
 * usb_gadget_set_selfpowered - sets the device selfpowered feature.
 * usb_gadget_set_selfpowered - sets the device selfpowered feature.
 * @gadget:the device being declared as self-powered
 * @gadget:the device being declared as self-powered
@@ -988,6 +1009,24 @@ void usb_free_all_descriptors(struct usb_function *f);


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


/**
 * 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 */
/* utility to simplify map/unmap of usb_requests to/from DMA */


extern int usb_gadget_map_request(struct usb_gadget *gadget,
extern int usb_gadget_map_request(struct usb_gadget *gadget,