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

Commit 65479f8f 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: Allow up to 900mA current draw for SuperSpeed"

parents ad16cf89 002923b8
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -96,8 +96,8 @@ config USB_GADGET_DEBUG_FS
	   to conserve kernel memory, say "N".

config USB_GADGET_VBUS_DRAW
	int "Maximum VBUS Power usage (2-500 mA)"
	range 2 500
	int "Maximum VBUS Power usage (2-900 mA)"
	range 2 900
	default 2
	help
	   Some devices need to draw power from USB when they are
@@ -106,7 +106,9 @@ config USB_GADGET_VBUS_DRAW
	   such as an AC adapter or batteries.

	   Enter the maximum power your device draws through USB, in
	   milliAmperes.  The permitted range of values is 2 - 500 mA;
	   milliAmperes.  The permitted range of values depends on the
	   connected speed: for SuperSpeed and up it is 2 - 900 mA, but
	   connections at High Speed or slower will be capped at 500 mA;
	   0 mA would be legal, but can make some hosts misbehave.

	   This value will be used except for system-specific gadget
+135 −3
Original line number Diff line number Diff line
@@ -266,6 +266,7 @@ int usb_add_function(struct usb_configuration *config,
		goto done;

	function->config = config;
	function->intf_id = -EINVAL;
	list_add_tail(&function->list, &config->functions);

	if (function->bind_deactivated) {
@@ -419,6 +420,8 @@ int usb_interface_id(struct usb_configuration *config,

	if (id < MAX_CONFIG_INTERFACES) {
		config->interface[id] = function;
		if (function->intf_id < 0)
			function->intf_id = id;
		config->next_interface_id = id + 1;
		return id;
	}
@@ -426,6 +429,101 @@ int usb_interface_id(struct usb_configuration *config,
}
EXPORT_SYMBOL_GPL(usb_interface_id);

static int usb_func_wakeup_int(struct usb_function *func)
{
	int ret;
	struct usb_gadget *gadget;

	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;

	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_gadget_func_wakeup(gadget, func->intf_id);

	return ret;
}

int usb_func_wakeup(struct usb_function *func)
{
	int ret;
	unsigned long flags;

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

	spin_lock_irqsave(&func->config->cdev->lock, flags);
	ret = usb_func_wakeup_int(func);
	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);
	}

	spin_unlock_irqrestore(&func->config->cdev->lock, flags);
	return ret;
}
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 || !func->config || !func->config->cdev ||
			!func->config->cdev->gadget || !ep || !req) {
		ret = -EINVAL;
		goto done;
	}

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

	gadget = func->config->cdev->gadget;
	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);
		}
		goto done;
	}

	if (!func->func_is_suspended)
		ret = 0;

	if (func->func_is_suspended && !func->func_wakeup_allowed) {
		ret = -ENOTSUPP;
		goto done;
	}

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

static u8 encode_bMaxPower(enum usb_device_speed speed,
		struct usb_configuration *c)
{
@@ -441,7 +539,8 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
	case USB_SPEED_SUPER:
		return DIV_ROUND_UP(val, 8);
	default:
		return DIV_ROUND_UP(val, 2);
		/* only SuperSpeed and faster support > 500mA */
		return DIV_ROUND_UP(min(val, 500U), 2);
	}
}

@@ -743,6 +842,11 @@ 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;
		f->func_wakeup_pending = false;

		bitmap_zero(f->endpoints, 32);
	}
	cdev->config = NULL;
@@ -1797,8 +1901,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",
@@ -2256,11 +2365,13 @@ void composite_suspend(struct usb_gadget *gadget)
{
	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
	struct usb_function		*f;
	unsigned long			flags;

	/* REVISIT:  should we have config level
	 * suspend/resume callbacks?
	 */
	DBG(cdev, "suspend\n");
	spin_lock_irqsave(&cdev->lock, flags);
	if (cdev->config) {
		list_for_each_entry(f, &cdev->config->functions, list) {
			if (f->suspend)
@@ -2271,6 +2382,7 @@ void composite_suspend(struct usb_gadget *gadget)
		cdev->driver->suspend(cdev);

	cdev->suspended = 1;
	spin_unlock_irqrestore(&cdev->lock, flags);

	usb_gadget_vbus_draw(gadget, 2);
}
@@ -2280,6 +2392,8 @@ void composite_resume(struct usb_gadget *gadget)
	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
	struct usb_function		*f;
	u16				maxpower;
	int				ret;
	unsigned long			flags;

	/* REVISIT:  should we have config level
	 * suspend/resume callbacks?
@@ -2287,8 +2401,25 @@ void composite_resume(struct usb_gadget *gadget)
	DBG(cdev, "resume\n");
	if (cdev->driver->resume)
		cdev->driver->resume(cdev);

	spin_lock_irqsave(&cdev->lock, flags);
	if (cdev->config) {
		list_for_each_entry(f, &cdev->config->functions, list) {
			ret = usb_func_wakeup_int(f);
			if (ret) {
				if (ret == -EAGAIN) {
					ERROR(f->config->cdev,
						"Function wakeup for %s could not complete due to suspend state.\n",
						f->name ? f->name : "");
					break;
				} else if (ret != -ENOTSUPP) {
					ERROR(f->config->cdev,
						"Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
						f->name ? f->name : "",
						ret);
				}
			}

			if (f->resume)
				f->resume(f);
		}
@@ -2299,6 +2430,7 @@ void composite_resume(struct usb_gadget *gadget)
			maxpower : CONFIG_USB_GADGET_VBUS_DRAW);
	}

	spin_unlock_irqrestore(&cdev->lock, flags);
	cdev->suspended = 0;
}

+21 −0
Original line number Diff line number Diff line
@@ -479,6 +479,27 @@ int usb_gadget_wakeup(struct usb_gadget *gadget)
}
EXPORT_SYMBOL_GPL(usb_gadget_wakeup);

/**
 * 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.
 */
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);
}
EXPORT_SYMBOL(usb_gadget_func_wakeup);

/**
 * usb_gadget_set_selfpowered - sets the device selfpowered feature.
 * @gadget:the device being declared as self-powered
+16 −1
Original line number Diff line number Diff line
@@ -114,6 +114,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
@@ -158,7 +159,14 @@ 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).
 * @func_wakeup_pending: Marks that the function has issued a Function Wakeup
 *	while the USB bus was suspended and therefore a Function Wakeup
 *	notification needs to be sent once the USB bus is resumed.
 *
 * 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
@@ -186,6 +194,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;
@@ -229,6 +238,9 @@ 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;
	unsigned		func_wakeup_pending:1;
	/* private: */
	/* internals */
	struct list_head		list;
@@ -244,6 +256,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);
+22 −0
Original line number Diff line number Diff line
@@ -297,6 +297,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);
@@ -544,6 +545,7 @@ static inline int gadget_is_otg(struct usb_gadget *g)
#if IS_ENABLED(CONFIG_USB_GADGET)
int usb_gadget_frame_number(struct usb_gadget *gadget);
int usb_gadget_wakeup(struct usb_gadget *gadget);
int usb_gadget_func_wakeup(struct usb_gadget *gadget, int interface_id);
int usb_gadget_set_selfpowered(struct usb_gadget *gadget);
int usb_gadget_clear_selfpowered(struct usb_gadget *gadget);
int usb_gadget_vbus_connect(struct usb_gadget *gadget);
@@ -558,6 +560,8 @@ static inline int usb_gadget_frame_number(struct usb_gadget *gadget)
{ return 0; }
static inline int usb_gadget_wakeup(struct usb_gadget *gadget)
{ return 0; }
static int usb_gadget_func_wakeup(struct usb_gadget *gadget, int interface_id)
{ return 0; }
static inline int usb_gadget_set_selfpowered(struct usb_gadget *gadget)
{ return 0; }
static inline int usb_gadget_clear_selfpowered(struct usb_gadget *gadget)
@@ -796,6 +800,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,