USB: core: Add API to change the wireless_status
authorBastien Nocera <hadess@hadess.net>
Thu, 2 Mar 2023 10:55:54 +0000 (11:55 +0100)
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>
Mon, 3 Apr 2023 11:30:32 +0000 (13:30 +0200)
This adds the API that allows device specific drivers to tell user-space
about whether the wireless device is connected to its receiver dongle.

See "USB: core: Add wireless_status sysfs attribute" for a detailed
explanation of what this attribute should be used for.

Signed-off-by: Bastien Nocera <hadess@hadess.net>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://lore.kernel.org/r/20230302105555.51417-5-hadess@hadess.net
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
drivers/usb/core/message.c
include/linux/usb.h

index cc404bb..33392a6 100644 (file)
@@ -1908,6 +1908,45 @@ static void __usb_queue_reset_device(struct work_struct *ws)
        usb_put_intf(iface);    /* Undo _get_ in usb_queue_reset_device() */
 }
 
+/*
+ * Internal function to set the wireless_status sysfs attribute
+ * See usb_set_wireless_status() for more details
+ */
+static void __usb_wireless_status_intf(struct work_struct *ws)
+{
+       struct usb_interface *iface =
+               container_of(ws, struct usb_interface, wireless_status_work);
+
+       device_lock(iface->dev.parent);
+       if (iface->sysfs_files_created)
+               usb_update_wireless_status_attr(iface);
+       device_unlock(iface->dev.parent);
+       usb_put_intf(iface);    /* Undo _get_ in usb_set_wireless_status() */
+}
+
+/**
+ * usb_set_wireless_status - sets the wireless_status struct member
+ * @dev: the device to modify
+ * @status: the new wireless status
+ *
+ * Set the wireless_status struct member to the new value, and emit
+ * sysfs changes as necessary.
+ *
+ * Returns: 0 on success, -EALREADY if already set.
+ */
+int usb_set_wireless_status(struct usb_interface *iface,
+               enum usb_wireless_status status)
+{
+       if (iface->wireless_status == status)
+               return -EALREADY;
+
+       usb_get_intf(iface);
+       iface->wireless_status = status;
+       schedule_work(&iface->wireless_status_work);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_set_wireless_status);
 
 /*
  * usb_set_configuration - Makes a particular device setting be current
@@ -2100,6 +2139,7 @@ free_interfaces:
                intf->dev.type = &usb_if_device_type;
                intf->dev.groups = usb_interface_groups;
                INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
+               INIT_WORK(&intf->wireless_status_work, __usb_wireless_status_intf);
                intf->minor = -1;
                device_initialize(&intf->dev);
                pm_runtime_no_callbacks(&intf->dev);
index eee54cc..6dfd59d 100644 (file)
@@ -262,6 +262,7 @@ struct usb_interface {
        unsigned resetting_device:1;    /* true: bandwidth alloc after reset */
        unsigned authorized:1;          /* used for interface authorization */
        enum usb_wireless_status wireless_status;
+       struct work_struct wireless_status_work;
 
        struct device dev;              /* interface specific device info */
        struct device *usb_dev;
@@ -896,6 +897,10 @@ static inline int usb_interface_claimed(struct usb_interface *iface)
 
 extern void usb_driver_release_interface(struct usb_driver *driver,
                        struct usb_interface *iface);
+
+int usb_set_wireless_status(struct usb_interface *iface,
+                       enum usb_wireless_status status);
+
 const struct usb_device_id *usb_match_id(struct usb_interface *interface,
                                         const struct usb_device_id *id);
 extern int usb_match_one_id(struct usb_interface *interface,