usb: Export BOS descriptor to sysfs

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>
This commit is contained in:
Elbert Mai 2024-03-04 16:23:01 -08:00 committed by Greg Kroah-Hartman
parent a14e6fd1b6
commit 12fc84e8c4
2 changed files with 71 additions and 17 deletions

View 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.

View 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 struct bin_attribute dev_bin_attr_descriptors = {
.attr = {.name = "descriptors", .mode = 0444},
.read = read_descriptors,
.size = 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 */
/* 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 */