From 6c97abe0f035529510a4b4cf60fd0655d2d5fc41 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Tue, 21 Jan 2020 19:26:11 -0800 Subject: [PATCH] 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 --- drivers/usb/gadget/Kconfig | 11 +++++++++ drivers/usb/gadget/composite.c | 45 ++++++++++++++++++++++++++++++++++ drivers/usb/gadget/udc/core.c | 13 ++++++++++ include/linux/usb/composite.h | 9 +++++++ include/linux/usb/gadget.h | 12 +++++++++ 5 files changed, 90 insertions(+) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 4cd21810e280..1d0c5632ff99 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -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 diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 5ec54b69c29c..3dbd107533e8 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -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) { diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 51fa614b4079..bba8bab8a6f0 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -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 diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index af4396cc4ea8..3d88ac9d3221 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -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); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index df42a0c2e8f1..0e9ab24644c7 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -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 + /*-------------------------------------------------------------------------*/ /**