]> git.itanic.dy.fi Git - linux-stable/commitdiff
usb: gadget: Properly configure the device for remote wakeup
authorElson Roy Serrao <quic_eserrao@quicinc.com>
Fri, 24 Mar 2023 21:47:57 +0000 (14:47 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 May 2023 12:55:29 +0000 (13:55 +0100)
[ Upstream commit b93c2a68f3d9dc98ec30dcb342ae47c1c8d09d18 ]

The wakeup bit in the bmAttributes field indicates whether the device
is configured for remote wakeup. But this field should be allowed to
set only if the UDC supports such wakeup mechanism. So configure this
field based on UDC capability. Also inform the UDC whether the device
is configured for remote wakeup by implementing a gadget op.

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-2-git-send-email-quic_eserrao@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Stable-dep-of: 4e8ef34e36f2 ("usb: dwc3: fix gadget mode suspend interrupt handler issue")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/gadget/composite.c
drivers/usb/gadget/configfs.c
drivers/usb/gadget/udc/core.c
drivers/usb/gadget/udc/trace.h
include/linux/usb/composite.h
include/linux/usb/gadget.h

index 553382ce38378fac3af220a10e61cf4fcfdb91f6..0886cff9aa1c08dc8dba15edefee3d9e4f79fb25 100644 (file)
@@ -498,6 +498,19 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
                return min(val, 900U) / 8;
 }
 
+void check_remote_wakeup_config(struct usb_gadget *g,
+                               struct usb_configuration *c)
+{
+       if (USB_CONFIG_ATT_WAKEUP & c->bmAttributes) {
+               /* Reset the rw bit if gadget is not capable of it */
+               if (!g->wakeup_capable && g->ops->set_remote_wakeup) {
+                       WARN(c->cdev, "Clearing wakeup bit for config c.%d\n",
+                            c->bConfigurationValue);
+                       c->bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
+               }
+       }
+}
+
 static int config_buf(struct usb_configuration *config,
                enum usb_device_speed speed, void *buf, u8 type)
 {
@@ -945,6 +958,11 @@ static int set_config(struct usb_composite_dev *cdev,
                power = min(power, 500U);
        else
                power = min(power, 900U);
+
+       if (USB_CONFIG_ATT_WAKEUP & c->bmAttributes)
+               usb_gadget_set_remote_wakeup(gadget, 1);
+       else
+               usb_gadget_set_remote_wakeup(gadget, 0);
 done:
        if (power <= USB_SELF_POWER_VBUS_MAX_DRAW)
                usb_gadget_set_selfpowered(gadget);
index 5cbf4084daedcfe84b8fe11999448f2aa955e2ee..528b9ec1d9e852a58715cc138979471f53293e39 100644 (file)
@@ -1384,6 +1384,9 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
                if (gadget_is_otg(gadget))
                        c->descriptors = otg_desc;
 
+               /* Properly configure the bmAttributes wakeup bit */
+               check_remote_wakeup_config(gadget, c);
+
                cfg = container_of(c, struct config_usb_cfg, c);
                if (!list_empty(&cfg->string_list)) {
                        i = 0;
index 61099f2d057dc9f1e6aea34a4bc75725923d3b9b..6c05a3a9b542f359213cf056dbeb850d120ea12d 100644 (file)
@@ -508,6 +508,33 @@ int usb_gadget_wakeup(struct usb_gadget *gadget)
 }
 EXPORT_SYMBOL_GPL(usb_gadget_wakeup);
 
+/**
+ * usb_gadget_set_remote_wakeup - configures the device remote wakeup feature.
+ * @gadget:the device being configured for remote wakeup
+ * @set:value to be configured.
+ *
+ * set to one to enable remote wakeup feature and zero to disable it.
+ *
+ * returns zero on success, else negative errno.
+ */
+int usb_gadget_set_remote_wakeup(struct usb_gadget *gadget, int set)
+{
+       int ret = 0;
+
+       if (!gadget->ops->set_remote_wakeup) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       ret = gadget->ops->set_remote_wakeup(gadget, set);
+
+out:
+       trace_usb_gadget_set_remote_wakeup(gadget, ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_set_remote_wakeup);
+
 /**
  * usb_gadget_set_selfpowered - sets the device selfpowered feature.
  * @gadget:the device being declared as self-powered
index 98584f6b6c66285afa5c19be5dbe2a5300fa08e7..428819311afbf0e7c59a6793f170f165e0543d7d 100644 (file)
@@ -91,6 +91,11 @@ DEFINE_EVENT(udc_log_gadget, usb_gadget_wakeup,
        TP_ARGS(g, ret)
 );
 
+DEFINE_EVENT(udc_log_gadget, usb_gadget_set_remote_wakeup,
+       TP_PROTO(struct usb_gadget *g, int ret),
+       TP_ARGS(g, ret)
+);
+
 DEFINE_EVENT(udc_log_gadget, usb_gadget_set_selfpowered,
        TP_PROTO(struct usb_gadget *g, int ret),
        TP_ARGS(g, ret)
index 9d2762279286718628e660327311f8b722183bd9..0399d1226323b8650bf40442a2273326efa47246 100644 (file)
@@ -426,6 +426,8 @@ extern int composite_dev_prepare(struct usb_composite_driver *composite,
 extern int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
                                         struct usb_ep *ep0);
 void composite_dev_cleanup(struct usb_composite_dev *cdev);
+void check_remote_wakeup_config(struct usb_gadget *g,
+                               struct usb_configuration *c);
 
 static inline struct usb_composite_driver *to_cdriver(
                struct usb_gadget_driver *gdrv)
index 10fe57cf40beca6a9ef8a5244975c4a6b3d590c8..c5bc739266ed6af28541e7cd67d20df318890dab 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     (*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);
        int     (*vbus_draw) (struct usb_gadget *, unsigned mA);
@@ -385,6 +386,8 @@ struct usb_gadget_ops {
  * @connected: True if gadget is connected.
  * @lpm_capable: If the gadget max_speed is FULL or HIGH, this flag
  *     indicates that it supports LPM as per the LPM ECN & errata.
+ * @wakeup_capable: True if gadget is capable of sending remote wakeup.
+ * @wakeup_armed: True if gadget is armed by the host for remote wakeup.
  * @irq: the interrupt number for device controller.
  *
  * Gadgets have a mostly-portable "gadget driver" implementing device
@@ -445,6 +448,8 @@ struct usb_gadget {
        unsigned                        deactivated:1;
        unsigned                        connected:1;
        unsigned                        lpm_capable:1;
+       unsigned                        wakeup_capable:1;
+       unsigned                        wakeup_armed:1;
        int                             irq;
 };
 #define work_to_gadget(w)      (container_of((w), struct usb_gadget, work))
@@ -600,6 +605,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_set_remote_wakeup(struct usb_gadget *gadget, int set);
 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);
@@ -615,6 +621,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 inline int usb_gadget_set_remote_wakeup(struct usb_gadget *gadget, int set)
+{ 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)