usb: gadget: Add function wakeup support

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. 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.

Change-Id: I72b3a25cfffa6060f1e10479e3c0ad9c0409aa45
Signed-off-by: Jack Pham <jackp@codeaurora.org>
This commit is contained in:
Jack Pham 2020-01-21 19:26:11 -08:00
commit 6c97abe0f0
5 changed files with 90 additions and 0 deletions

View file

@ -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

View file

@ -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)
{

View file

@ -807,6 +807,19 @@ out:
}
EXPORT_SYMBOL_GPL(usb_gadget_activate);
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
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);
}
#endif
/* ------------------------------------------------------------------------- */
#ifdef CONFIG_HAS_DMA

View file

@ -249,6 +249,15 @@ int usb_function_activate(struct usb_function *);
int usb_interface_id(struct usb_configuration *, struct usb_function *);
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
int usb_func_wakeup(struct usb_function *func);
#else
static inline int usb_func_wakeup(struct usb_function *func)
{
return -EOPNOTSUPP;
}
#endif
int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
struct usb_ep *_ep);

View file

@ -323,6 +323,9 @@ struct usb_gadget_ops {
struct usb_ep *(*match_ep)(struct usb_gadget *,
struct usb_endpoint_descriptor *,
struct usb_ss_ep_comp_descriptor *);
#ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED
int (*func_wakeup)(struct usb_gadget *, int interface_id);
#endif
};
/**
@ -595,6 +598,15 @@ static inline int usb_gadget_activate(struct usb_gadget *gadget)
{ return 0; }
#endif /* CONFIG_USB_GADGET */
#if IS_ENABLED(CONFIG_USB_GADGET) && \
IS_BUILTIN(CONFIG_USB_FUNC_WAKEUP_SUPPORTED)
int usb_gadget_func_wakeup(struct usb_gadget *gadget, int interface_id);
#else
static inline int usb_gadget_func_wakeup(struct usb_gadget *gadget,
int interface_id)
{ return 0; }
#endif
/*-------------------------------------------------------------------------*/
/**