Skip to content

Commit f98e064

Browse files
hadessbentiss
authored andcommitted
USB: core: Add wireless_status sysfs attribute
Add a wireless_status sysfs attribute to USB devices to keep track of whether a USB device that's comprised of a receiver dongle and an emitter device over a, most of the time proprietary, wireless link has its emitter connected or disconnected. This will be used by user-space OS components to determine whether the battery-powered part of the device is wirelessly connected or not, allowing, for example: - upower to hide the battery for devices where the device is turned off but the receiver plugged in, rather than showing 0%, or other values that could be confusing to users - Pipewire to hide a headset from the list of possible inputs or outputs or route audio appropriately if the headset is suddenly turned off, or turned on - libinput to determine whether a keyboard or mouse is present when its receiver is plugged in. This is done at the USB interface level as: - the interface on which the wireless status is detected is sometimes not the same as where it could be consumed (eg. the audio interface on a headset dongle will still appear even if the headset is turned off), and we cannot have synchronisation of status across subsystems. - this behaviour is not specific to HID devices, even if the protocols used to determine whether or not the remote device is connected can be HID. This is not an attribute that is meant to replace protocol specific APIs, such as the ones available for WWAN, WLAN/Wi-Fi, or Bluetooth or any other sort of networking, but solely for wireless devices with an ad-hoc “lose it and your device is e-waste” receiver dongle. The USB interface will only be exporting the wireless_status sysfs attribute if it gets set through the API exported in the next commit. 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-4-hadess@hadess.net Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
1 parent 4a1529f commit f98e064

4 files changed

Lines changed: 77 additions & 0 deletions

File tree

Documentation/ABI/testing/sysfs-bus-usb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,23 @@ Description:
166166
The file will be present for all speeds of USB devices, and will
167167
always read "no" for USB 1.1 and USB 2.0 devices.
168168

169+
What: /sys/bus/usb/devices/<INTERFACE>/wireless_status
170+
Date: February 2023
171+
Contact: Bastien Nocera <hadess@hadess.net>
172+
Description:
173+
Some USB devices use a USB receiver dongle to communicate
174+
wirelessly with their device using proprietary protocols. This
175+
attribute allows user-space to know whether the device is
176+
connected to its receiver dongle, and, for example, consider
177+
the device to be absent when choosing whether to show the
178+
device's battery, show a headset in a list of outputs, or show
179+
an on-screen keyboard if the only wireless keyboard is
180+
turned off.
181+
This attribute is not to be used to replace protocol specific
182+
statuses available in WWAN, WLAN/Wi-Fi, Bluetooth, etc.
183+
If the device does not use a receiver dongle with a wireless
184+
device, then this attribute will not exist.
185+
169186
What: /sys/bus/usb/devices/.../<hub_interface>/port<X>
170187
Date: August 2012
171188
Contact: Lan Tianyu <tianyu.lan@intel.com>

drivers/usb/core/sysfs.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,9 +1227,59 @@ static const struct attribute_group intf_assoc_attr_grp = {
12271227
.is_visible = intf_assoc_attrs_are_visible,
12281228
};
12291229

1230+
static ssize_t wireless_status_show(struct device *dev,
1231+
struct device_attribute *attr, char *buf)
1232+
{
1233+
struct usb_interface *intf;
1234+
1235+
intf = to_usb_interface(dev);
1236+
if (intf->wireless_status == USB_WIRELESS_STATUS_DISCONNECTED)
1237+
return sysfs_emit(buf, "%s\n", "disconnected");
1238+
return sysfs_emit(buf, "%s\n", "connected");
1239+
}
1240+
static DEVICE_ATTR_RO(wireless_status);
1241+
1242+
static struct attribute *intf_wireless_status_attrs[] = {
1243+
&dev_attr_wireless_status.attr,
1244+
NULL
1245+
};
1246+
1247+
static umode_t intf_wireless_status_attr_is_visible(struct kobject *kobj,
1248+
struct attribute *a, int n)
1249+
{
1250+
struct device *dev = kobj_to_dev(kobj);
1251+
struct usb_interface *intf = to_usb_interface(dev);
1252+
1253+
if (a != &dev_attr_wireless_status.attr ||
1254+
intf->wireless_status != USB_WIRELESS_STATUS_NA)
1255+
return a->mode;
1256+
return 0;
1257+
}
1258+
1259+
static const struct attribute_group intf_wireless_status_attr_grp = {
1260+
.attrs = intf_wireless_status_attrs,
1261+
.is_visible = intf_wireless_status_attr_is_visible,
1262+
};
1263+
1264+
int usb_update_wireless_status_attr(struct usb_interface *intf)
1265+
{
1266+
struct device *dev = &intf->dev;
1267+
int ret;
1268+
1269+
ret = sysfs_update_group(&dev->kobj, &intf_wireless_status_attr_grp);
1270+
if (ret < 0)
1271+
return ret;
1272+
1273+
sysfs_notify(&dev->kobj, NULL, "wireless_status");
1274+
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
1275+
1276+
return 0;
1277+
}
1278+
12301279
const struct attribute_group *usb_interface_groups[] = {
12311280
&intf_attr_grp,
12321281
&intf_assoc_attr_grp,
1282+
&intf_wireless_status_attr_grp,
12331283
NULL
12341284
};
12351285

drivers/usb/core/usb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern int usb_create_sysfs_dev_files(struct usb_device *dev);
1515
extern void usb_remove_sysfs_dev_files(struct usb_device *dev);
1616
extern void usb_create_sysfs_intf_files(struct usb_interface *intf);
1717
extern void usb_remove_sysfs_intf_files(struct usb_interface *intf);
18+
extern int usb_update_wireless_status_attr(struct usb_interface *intf);
1819
extern int usb_create_ep_devs(struct device *parent,
1920
struct usb_host_endpoint *endpoint,
2021
struct usb_device *udev);

include/linux/usb.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ usb_find_last_int_out_endpoint(struct usb_host_interface *alt,
170170
return usb_find_common_endpoints_reverse(alt, NULL, NULL, NULL, int_out);
171171
}
172172

173+
enum usb_wireless_status {
174+
USB_WIRELESS_STATUS_NA = 0,
175+
USB_WIRELESS_STATUS_DISCONNECTED,
176+
USB_WIRELESS_STATUS_CONNECTED,
177+
};
178+
173179
/**
174180
* struct usb_interface - what usb device drivers talk to
175181
* @altsetting: array of interface structures, one for each alternate
@@ -203,6 +209,8 @@ usb_find_last_int_out_endpoint(struct usb_host_interface *alt,
203209
* @reset_ws: Used for scheduling resets from atomic context.
204210
* @resetting_device: USB core reset the device, so use alt setting 0 as
205211
* current; needs bandwidth alloc after reset.
212+
* @wireless_status: if the USB device uses a receiver/emitter combo, whether
213+
* the emitter is connected.
206214
*
207215
* USB device drivers attach to interfaces on a physical device. Each
208216
* interface encapsulates a single high level function, such as feeding
@@ -253,6 +261,7 @@ struct usb_interface {
253261
unsigned needs_binding:1; /* needs delayed unbind/rebind */
254262
unsigned resetting_device:1; /* true: bandwidth alloc after reset */
255263
unsigned authorized:1; /* used for interface authorization */
264+
enum usb_wireless_status wireless_status;
256265

257266
struct device dev; /* interface specific device info */
258267
struct device *usb_dev;

0 commit comments

Comments
 (0)