usb: Export BOS descriptor to sysfs
authorElbert Mai <code@elbertmai.com>
Tue, 5 Mar 2024 00:23:01 +0000 (16:23 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 5 Mar 2024 07:57:23 +0000 (07:57 +0000)
Motivation
----------

The binary device object store (BOS) of a USB device consists of the BOS
descriptor followed by a set of device capability descriptors. One that is
of interest to users is the platform descriptor. This contains a 128-bit
UUID and arbitrary data, and it allows parties outside of USB-IF to add
additional metadata about a USB device in a standards-compliant manner.
Notable examples include the WebUSB and Microsoft OS 2.0 descriptors.

The kernel already retrieves and caches the BOS from USB devices if its
bcdUSB is >= 0x0201. Because the BOS is flexible and extensible, we export
the entire BOS to sysfs so users can retrieve whatever device capabilities
they desire, without requiring USB I/O or elevated permissions.

Implementation
--------------

Add bos_descriptors attribute to sysfs. This is a binary file and it works
the same way as the existing descriptors attribute. The file exists only if
the BOS is present in the USB device.

Also create a binary attribute group, so the driver core can handle the
creation of both the descriptors and bos_descriptors attributes in sysfs.

Signed-off-by: Elbert Mai <code@elbertmai.com>
Link: https://lore.kernel.org/r/20240305002301.95323-1-code@elbertmai.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/ABI/testing/sysfs-bus-usb
drivers/usb/core/sysfs.c

index 2b7108e2197756db86cfbd83aa3370457b4f4bf1..af9b653422f169f18b2db09905f3e4a659c9aa6e 100644 (file)
@@ -442,6 +442,16 @@ What:              /sys/bus/usb/devices/usbX/descriptors
 Description:
                Contains the interface descriptors, in binary.
 
+What:          /sys/bus/usb/devices/usbX/bos_descriptors
+Date:          March 2024
+Contact:       Elbert Mai <code@elbertmai.com>
+Description:
+               Binary file containing the cached binary device object store (BOS)
+               of the device. This consists of the BOS descriptor followed by the
+               set of device capability descriptors. All descriptors read from
+               this file are in bus-endian format. Note that the kernel will not
+               request the BOS from a device if its bcdUSB is less than 0x0201.
+
 What:          /sys/bus/usb/devices/usbX/idProduct
 Description:
                Product ID, in hexadecimal.
index e67826ec053e19f1e90112bb3fb2c254febf7b6f..777526f5972064dd43bd26168c05e17b9628269e 100644 (file)
@@ -849,16 +849,10 @@ static const struct attribute_group dev_string_attr_grp = {
        .is_visible =   dev_string_attrs_are_visible,
 };
 
-const struct attribute_group *usb_device_groups[] = {
-       &dev_attr_grp,
-       &dev_string_attr_grp,
-       NULL
-};
-
 /* Binary descriptors */
 
 static ssize_t
-read_descriptors(struct file *filp, struct kobject *kobj,
+descriptors_read(struct file *filp, struct kobject *kobj,
                struct bin_attribute *attr,
                char *buf, loff_t off, size_t count)
 {
@@ -880,7 +874,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
                        srclen = sizeof(struct usb_device_descriptor);
                } else {
                        src = udev->rawdescriptors[cfgno];
-                       srclen = __le16_to_cpu(udev->config[cfgno].desc.
+                       srclen = le16_to_cpu(udev->config[cfgno].desc.
                                        wTotalLength);
                }
                if (off < srclen) {
@@ -895,11 +889,66 @@ read_descriptors(struct file *filp, struct kobject *kobj,
        }
        return count - nleft;
 }
+static BIN_ATTR_RO(descriptors, 18 + 65535); /* dev descr + max-size raw descriptor */
+
+static ssize_t
+bos_descriptors_read(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr,
+               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct usb_device *udev = to_usb_device(dev);
+       struct usb_host_bos *bos = udev->bos;
+       struct usb_bos_descriptor *desc;
+       size_t desclen, n = 0;
+
+       if (bos) {
+               desc = bos->desc;
+               desclen = le16_to_cpu(desc->wTotalLength);
+               if (off < desclen) {
+                       n = min(count, desclen - (size_t) off);
+                       memcpy(buf, (void *) desc + off, n);
+               }
+       }
+       return n;
+}
+static BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
 
-static struct bin_attribute dev_bin_attr_descriptors = {
-       .attr = {.name = "descriptors", .mode = 0444},
-       .read = read_descriptors,
-       .size = 18 + 65535,     /* dev descr + max-size raw descriptor */
+/* When modifying this list, be sure to modify dev_bin_attrs_are_visible()
+ * accordingly.
+ */
+static struct bin_attribute *dev_bin_attrs[] = {
+       &bin_attr_descriptors,
+       &bin_attr_bos_descriptors,
+       NULL
+};
+
+static umode_t dev_bin_attrs_are_visible(struct kobject *kobj,
+               struct bin_attribute *a, int n)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct usb_device *udev = to_usb_device(dev);
+
+       /* All USB devices have a device descriptor, so the descriptors
+        * attribute always exists. No need to check for its visibility.
+        */
+       if (a == &bin_attr_bos_descriptors) {
+               if (udev->bos == NULL)
+                       return 0;
+       }
+       return a->attr.mode;
+}
+
+static const struct attribute_group dev_bin_attr_grp = {
+       .bin_attrs =            dev_bin_attrs,
+       .is_bin_visible =       dev_bin_attrs_are_visible,
+};
+
+const struct attribute_group *usb_device_groups[] = {
+       &dev_attr_grp,
+       &dev_string_attr_grp,
+       &dev_bin_attr_grp,
+       NULL
 };
 
 /*
@@ -1017,10 +1066,6 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
        struct device *dev = &udev->dev;
        int retval;
 
-       retval = device_create_bin_file(dev, &dev_bin_attr_descriptors);
-       if (retval)
-               goto error;
-
        retval = add_persist_attributes(dev);
        if (retval)
                goto error;
@@ -1050,7 +1095,6 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
 
        remove_power_attributes(dev);
        remove_persist_attributes(dev);
-       device_remove_bin_file(dev, &dev_bin_attr_descriptors);
 }
 
 /* Interface Association Descriptor fields */