2021-02-22 21:25:02 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2020-08-08 16:27:58 +08:00
|
|
|
/*
|
|
|
|
* Helpers for querying USB properties from sysfs
|
2021-02-22 21:13:34 +08:00
|
|
|
*
|
|
|
|
* Copied from name.c which is:
|
|
|
|
* Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
|
|
|
* Copyright (C) 2013 Tom Gundersen (teg@jklm.no)
|
2020-08-08 16:27:58 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <linux/limits.h>
|
|
|
|
|
sysfs: Don't return bogus data for devices under a hub
commit dddb696e0172 ("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: dddb696e0172 ("lsusb: Read unkown names from sysfs device desc.")
Signed-off-by: Thomas Hebb <tommyhebb@gmail.com>
2020-08-08 17:10:22 +08:00
|
|
|
#include <libusb.h>
|
|
|
|
|
2020-08-08 16:27:58 +08:00
|
|
|
#include "sysfs.h"
|
|
|
|
|
sysfs: Don't return bogus data for devices under a hub
commit dddb696e0172 ("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: dddb696e0172 ("lsusb: Read unkown names from sysfs device desc.")
Signed-off-by: Thomas Hebb <tommyhebb@gmail.com>
2020-08-08 17:10:22 +08:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
|
|
|
#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);
|
2024-10-22 17:17:18 +08:00
|
|
|
for (int i = 0; i < num_pnums; i++) {
|
|
|
|
int n = snprintf(buf + len, size - len, i ? ".%d" : "%d", pnums[i]);
|
|
|
|
if ((n < 0) || (n >= (int)(size - len)))
|
|
|
|
break;
|
|
|
|
len += n;
|
|
|
|
}
|
sysfs: Don't return bogus data for devices under a hub
commit dddb696e0172 ("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: dddb696e0172 ("lsusb: Read unkown names from sysfs device desc.")
Signed-off-by: Thomas Hebb <tommyhebb@gmail.com>
2020-08-08 17:10:22 +08:00
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
2020-08-08 16:27:58 +08:00
|
|
|
|
2024-09-20 05:52:22 +08:00
|
|
|
int read_sysfs_prop(char *buf, size_t size, const char *sysfs_name, const char *propname)
|
2020-08-08 16:27:58 +08:00
|
|
|
{
|
|
|
|
int n, fd;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
|
|
|
|
buf[0] = '\0';
|
sysfs: Don't return bogus data for devices under a hub
commit dddb696e0172 ("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: dddb696e0172 ("lsusb: Read unkown names from sysfs device desc.")
Signed-off-by: Thomas Hebb <tommyhebb@gmail.com>
2020-08-08 17:10:22 +08:00
|
|
|
snprintf(path, sizeof(path), SYSFS_DEV_ATTR_PATH, sysfs_name, propname);
|
2020-08-08 16:27:58 +08:00
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
|
|
|
|
if (fd == -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
n = read(fd, buf, size);
|
|
|
|
|
|
|
|
if (n > 0)
|
|
|
|
buf[n-1] = '\0'; // Turn newline into null terminator
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return n;
|
|
|
|
}
|