diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 568103d3376e..23ba756d40f7 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -166,6 +166,17 @@ Description:
                The file will be present for all speeds of USB devices, and will
                always read "no" for USB 1.1 and USB 2.0 devices.
 
+What:          /sys/bus/usb/devices/.../wireless_status
+Date:          December 2022
+Contact:       Bastien Nocera <hadess@hadess.net>
+Description:
+               Some USB devices use a small USB receiver coupled with a larger
+               wireless device, usually communicating using proprietary
+               wireless protocols. This attribute will read either "connected"
+               or "disconnected" depending on whether the emitter is turned on,
+               in range and connected. If the device does not use a receiver/
+               emitter combo, then this attribute will not exist.
+
 What:          /sys/bus/usb/devices/.../<hub_interface>/port<X>
 Date:          August 2012
 Contact:       Lan Tianyu <tianyu.lan@intel.com>
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 631574718d8a..6e963fc9ed73 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -849,12 +849,62 @@ static const struct attribute_group dev_string_attr_grp = {
        .is_visible =   dev_string_attrs_are_visible,
 };
 
+static ssize_t wireless_status_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct usb_device *udev;
+
+       udev = to_usb_device(dev);
+       if (udev->wireless_status == USB_WIRELESS_STATUS_DISCONNECTED)
+               return sysfs_emit(buf, "%s\n", "disconnected");
+       return sysfs_emit(buf, "%s\n", "connected");
+}
+static DEVICE_ATTR_RO(wireless_status);
+
+static struct attribute *dev_wireless_status_attrs[] = {
+       &dev_attr_wireless_status.attr,
+       NULL
+};
+
+static umode_t dev_wireless_status_attr_is_visible(struct kobject *kobj,
+               struct attribute *a, int n)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct usb_device *udev = to_usb_device(dev);
+
+       if (a != &dev_attr_wireless_status.attr ||
+           udev->wireless_status != USB_WIRELESS_STATUS_NA)
+               return a->mode;
+       return 0;
+}
+
+static const struct attribute_group dev_wireless_status_attr_grp = {
+       .attrs =        dev_wireless_status_attrs,
+       .is_visible =   dev_wireless_status_attr_is_visible,
+};
+
 const struct attribute_group *usb_device_groups[] = {
        &dev_attr_grp,
        &dev_string_attr_grp,
+       &dev_wireless_status_attr_grp,
        NULL
 };
 
+int usb_update_wireless_status_attr(struct usb_device *udev)
+{
+       struct device *dev = &udev->dev;
+       int ret;
+
+       ret = sysfs_update_group(&dev->kobj, &dev_wireless_status_attr_grp);
+       if (ret < 0)
+               return ret;
+
+       sysfs_notify(&dev->kobj, NULL, "wireless_status");
+       kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+
+       return 0;
+}
+
 /* Binary descriptors */
 
 static ssize_t
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 0eac7d4285d1..33d42d1b7d99 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -13,6 +13,7 @@ struct usb_dev_state;
 
 extern int usb_create_sysfs_dev_files(struct usb_device *dev);
 extern void usb_remove_sysfs_dev_files(struct usb_device *dev);
+extern int usb_update_wireless_status_attr(struct usb_device *dev);
 extern void usb_create_sysfs_intf_files(struct usb_interface *intf);
 extern void usb_remove_sysfs_intf_files(struct usb_interface *intf);
 extern int usb_create_ep_devs(struct device *parent,
diff --git a/include/linux/usb.h b/include/linux/usb.h
index d2d2f41052c0..0c527cbd7165 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -545,6 +545,12 @@ struct usb3_lpm_parameters {
        int timeout;
 };
 
+enum usb_wireless_status {
+       USB_WIRELESS_STATUS_NA = 0,
+       USB_WIRELESS_STATUS_DISCONNECTED,
+       USB_WIRELESS_STATUS_CONNECTED,
+};
+
 /**
  * struct usb_device - kernel's representation of a USB device
  * @devnum: device number; address on a USB bus
@@ -620,6 +626,8 @@ struct usb3_lpm_parameters {
  *     parent->hub_delay + wHubDelay + tTPTransmissionDelay (40ns)
  *     Will be used as wValue for SetIsochDelay requests.
  * @use_generic_driver: ask driver core to reprobe using the generic driver.
+ * @wireless_status: if the USB device uses a receiver/emitter combo, whether
+ *     the emitter is connected.
  *
  * Notes:
  * Usbcore drivers should not set usbdev->state directly.  Instead use
@@ -708,6 +716,7 @@ struct usb_device {
 
        u16 hub_delay;
        unsigned use_generic_driver:1;
+       enum usb_wireless_status wireless_status;
 };
 #define        to_usb_device(d) container_of(d, struct usb_device, dev)
 
