sysfs: Don't return bogus data for devices under a hub

commit dddb696e01 ("lsusb: Read unkown names from sysfs device desc.")
added code to query a device's self-reported vendor and product names
from sysfs in the case where no names were present in the udev hwdb.
However, as far as I can tell that code was and still is totally broken
for any device that isn't directly under a root hub: this is because it
uses the libusb_get_port_number() function, which only returns the leaf
port number, but treats the returned number as if it's a full device
path.

For example, when trying to query sysfs for the device 2-8.3 (a device
on Port 3 of a hub that's on Port 8 of Bus 2), it will instead
erroneously query device 2-3, ignoring the first hub altogether. This
results in nonsensical output from lsusb, such as a device identified as
a "Bose Corp. Steam Controller".

To fix the issue, use the libusb_get_port_numbers() function to get a
full list of port numbers from the root and use that to construct the
proper dotted devpath. Additionally, special case root devices, which
have the sysfs name "usbN", where "N" is the bus number. Thanks to Tian
Yunhao, who submitted a different fix for this same issue, for spotting
that last piece.

Fixes: dddb696e01 ("lsusb: Read unkown names from sysfs device desc.")
Signed-off-by: Thomas Hebb <tommyhebb@gmail.com>
This commit is contained in:
Thomas Hebb 2020-08-08 02:10:22 -07:00
parent c37f33cf31
commit 151eea8fb4
3 changed files with 41 additions and 7 deletions

View File

@ -3703,7 +3703,8 @@ static int list_devices(libusb_context *ctx, int busnum, int devnum, int vendori
libusb_device *dev = list[i];
uint8_t bnum = libusb_get_bus_number(dev);
uint8_t dnum = libusb_get_device_address(dev);
uint8_t pnum = libusb_get_port_number(dev);
char sysfs_name[PATH_MAX];
get_sysfs_name(sysfs_name, sizeof(sysfs_name), dev);
if ((busnum != -1 && busnum != bnum) ||
(devnum != -1 && devnum != dnum))
@ -3716,13 +3717,13 @@ static int list_devices(libusb_context *ctx, int busnum, int devnum, int vendori
vendor_len = get_vendor_string(vendor, sizeof(vendor), desc.idVendor);
if (vendor_len == 0)
read_sysfs_prop(vendor, sizeof(vendor), bnum, pnum,
read_sysfs_prop(vendor, sizeof(vendor), sysfs_name,
"manufacturer");
product_len = get_product_string(product, sizeof(product),
desc.idVendor, desc.idProduct);
if (product_len == 0)
read_sysfs_prop(product, sizeof(product), bnum, pnum,
read_sysfs_prop(product, sizeof(product), sysfs_name,
"product");
if (verblevel > 0)

38
sysfs.c
View File

@ -10,17 +10,49 @@
#include <stdio.h>
#include <linux/limits.h>
#include <libusb.h>
#include "sysfs.h"
#define SYSFS_DEV_ATTR_PATH "/sys/bus/usb/devices/%d-%d/%s"
/*
* The documentation of libusb_get_port_numbers() says "As per the USB 3.0
* specs, the current maximum limit for the depth is 7."
*/
#define USB_MAX_DEPTH 7
int read_sysfs_prop(char *buf, size_t size, uint8_t bnum, uint8_t pnum, char *propname)
#define SYSFS_DEV_ATTR_PATH "/sys/bus/usb/devices/%s/%s"
int get_sysfs_name(char *buf, size_t size, libusb_device *dev)
{
int len = 0;
uint8_t bnum = libusb_get_bus_number(dev);
uint8_t pnums[USB_MAX_DEPTH];
int num_pnums;
buf[0] = '\0';
num_pnums = libusb_get_port_numbers(dev, pnums, sizeof(pnums));
if (num_pnums == LIBUSB_ERROR_OVERFLOW) {
return -1;
} else if (num_pnums == 0) {
/* Special-case root devices */
return snprintf(buf, size, "usb%d", bnum);
}
len += snprintf(buf, size, "%d-", bnum);
for (int i = 0; i < num_pnums; i++)
len += snprintf(buf + len, size - len, i ? ".%d" : "%d", pnums[i]);
return len;
}
int read_sysfs_prop(char *buf, size_t size, char *sysfs_name, char *propname)
{
int n, fd;
char path[PATH_MAX];
buf[0] = '\0';
snprintf(path, sizeof(path), SYSFS_DEV_ATTR_PATH, bnum, pnum, propname);
snprintf(path, sizeof(path), SYSFS_DEV_ATTR_PATH, sysfs_name, propname);
fd = open(path, O_RDONLY);
if (fd == -1)

View File

@ -7,7 +7,8 @@
#define _SYSFS_H
/* ---------------------------------------------------------------------- */
extern int read_sysfs_prop(char *buf, size_t size, uint8_t bnum, uint8_t pnum, char *propname);
int get_sysfs_name(char *buf, size_t size, libusb_device *dev);
extern int read_sysfs_prop(char *buf, size_t size, char *sysfs_name, char *propname);
/* ---------------------------------------------------------------------- */
#endif /* _SYSFS_H */