]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
usb: gadget: Add function wakeup support
authorElson Roy Serrao <quic_eserrao@quicinc.com>
Fri, 24 Mar 2023 21:47:59 +0000 (14:47 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 1 Nov 2024 00:52:35 +0000 (01:52 +0100)
[ Upstream commit f0db885fb05d35befa81896db6b19eb3ee9ccdfe ]

USB3.2 spec section 9.2.5.4 quotes that a function may signal that
it wants to exit from Function Suspend by sending a Function
Wake Notification to the host if it is enabled for function
remote wakeup. Add an api in composite layer that can be used
by the function drivers to support this feature. Also expose
a gadget op so that composite layer can trigger a wakeup request
to the UDC driver.

Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Elson Roy Serrao <quic_eserrao@quicinc.com>
Link: https://lore.kernel.org/r/1679694482-16430-4-git-send-email-quic_eserrao@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Stable-dep-of: 705e3ce37bcc ("usb: dwc3: core: Fix system suspend on TI AM62 platforms")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/gadget/composite.c
include/linux/usb/composite.h
include/linux/usb/gadget.h

index 3f035e905b24269b082800a6d9c1fe0079673ea8..1052ca4e29bc15e98a211faf2088dfe6d58f5e5d 100644 (file)
@@ -477,6 +477,46 @@ int usb_interface_id(struct usb_configuration *config,
 }
 EXPORT_SYMBOL_GPL(usb_interface_id);
 
+/**
+ * usb_func_wakeup - sends function wake notification to the host.
+ * @func: function that sends the remote wakeup notification.
+ *
+ * Applicable to devices operating at enhanced superspeed when usb
+ * functions are put in function suspend state and armed for function
+ * remote wakeup. On completion, function wake notification is sent. If
+ * the device is in low power state it tries to bring the device to active
+ * state before sending the wake notification. Since it is a synchronous
+ * call, caller must take care of not calling it in interrupt context.
+ * For devices operating at lower speeds  returns negative errno.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_func_wakeup(struct usb_function *func)
+{
+       struct usb_gadget       *gadget = func->config->cdev->gadget;
+       int                     id;
+
+       if (!gadget->ops->func_wakeup)
+               return -EOPNOTSUPP;
+
+       if (!func->func_wakeup_armed) {
+               ERROR(func->config->cdev, "not armed for func remote wakeup\n");
+               return -EINVAL;
+       }
+
+       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\n");
+               return -EINVAL;
+       }
+
+       return gadget->ops->func_wakeup(gadget, id);
+}
+EXPORT_SYMBOL_GPL(usb_func_wakeup);
+
 static u8 encode_bMaxPower(enum usb_device_speed speed,
                struct usb_configuration *c)
 {
index 0399d1226323b8650bf40442a2273326efa47246..456fca4d6a2534c321b73a9dafd0e9f00b076b52 100644 (file)
@@ -163,6 +163,9 @@ struct usb_os_desc_table {
  *     GetStatus() request when the recipient is Interface.
  * @func_suspend: callback to be called when
  *     SetFeature(FUNCTION_SUSPEND) is reseived
+ * @func_suspended: Indicates whether the function is in function suspend state.
+ * @func_wakeup_armed: Indicates whether the function is armed by the host for
+ *     wakeup signaling.
  *
  * 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
@@ -233,6 +236,8 @@ struct usb_function {
        int                     (*get_status)(struct usb_function *);
        int                     (*func_suspend)(struct usb_function *,
                                                u8 suspend_opt);
+       bool                    func_suspended;
+       bool                    func_wakeup_armed;
        /* private: */
        /* internals */
        struct list_head                list;
@@ -254,6 +259,7 @@ int config_ep_by_speed_and_alt(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);
+int usb_func_wakeup(struct usb_function *func);
 
 #define        MAX_CONFIG_INTERFACES           16      /* arbitrary; max 255 */
 
index c5bc739266ed6af28541e7cd67d20df318890dab..e4feeaa8bab308961c235a1021f65861aae3203e 100644 (file)
@@ -311,6 +311,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 *gadget, int intf_id);
        int     (*set_remote_wakeup)(struct usb_gadget *, int set);
        int     (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
        int     (*vbus_session) (struct usb_gadget *, int is_active);