mirror of
https://github.com/Cxbx-Reloaded/libusb.git
synced 2025-03-04 07:57:30 +00:00
Linux: Compatibility with new sysfs descriptors file
As of 2.6.26, the descriptors file now includes all descriptors, not just the active one.
This commit is contained in:
parent
819e65f880
commit
fbad9a5426
2
TODO
2
TODO
@ -1,5 +1,3 @@
|
||||
new sysfs descriptors format
|
||||
|
||||
for 1.1 or future
|
||||
==================
|
||||
optional timerfd support (runtime detection)
|
||||
|
@ -56,6 +56,10 @@
|
||||
*
|
||||
* If we also have descriptors, we can obtain the device descriptor and active
|
||||
* configuration without touching usbfs at all.
|
||||
*
|
||||
* The descriptors file originally only contained the active configuration
|
||||
* descriptor alongside the device descriptor, but all configurations are
|
||||
* included as of Linux 2.6.26.
|
||||
*/
|
||||
|
||||
static const char *usbfs_path = NULL;
|
||||
@ -262,12 +266,96 @@ static int usbfs_get_active_config_descriptor(struct libusb_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read the bConfigurationValue for a device */
|
||||
static int sysfs_get_active_config(struct libusb_device *dev, int *config)
|
||||
{
|
||||
char *endptr;
|
||||
char tmp[4] = {0, 0, 0, 0};
|
||||
long num;
|
||||
int fd;
|
||||
size_t r;
|
||||
|
||||
fd = __open_sysfs_attr(dev, "bConfigurationValue");
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = read(fd, tmp, sizeof(tmp));
|
||||
close(fd);
|
||||
if (r < 0) {
|
||||
usbi_err(DEVICE_CTX(dev),
|
||||
"read bConfigurationValue failed ret=%d errno=%d", r, errno);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (r == 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "device unconfigured");
|
||||
*config = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tmp[sizeof(tmp) - 1] != 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "not null-terminated?");
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (tmp[0] == 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "no configuration value?");
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
num = strtol(tmp, &endptr, 10);
|
||||
if (endptr == tmp) {
|
||||
usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
*config = (int) num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* takes a usbfs/descriptors fd seeked to the start of a configuration, and
|
||||
* seeks to the next one. */
|
||||
static int seek_to_next_config(struct libusb_context *ctx, int fd)
|
||||
{
|
||||
struct libusb_config_descriptor config;
|
||||
unsigned char tmp[6];
|
||||
off_t off;
|
||||
int r;
|
||||
|
||||
/* read first 6 bytes of descriptor */
|
||||
r = read(fd, tmp, sizeof(tmp));
|
||||
if (r < 0) {
|
||||
usbi_err(ctx, "read failed ret=%d errno=%d", r, errno);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (r < sizeof(tmp)) {
|
||||
usbi_err(ctx, "short descriptor read %d/%d", r, sizeof(tmp));
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
/* seek forward to end of config */
|
||||
usbi_parse_descriptor(tmp, "bbwbb", &config, 1);
|
||||
off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR);
|
||||
if (off < 0) {
|
||||
usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sysfs_get_active_config_descriptor(struct libusb_device *dev,
|
||||
unsigned char *buffer, size_t len)
|
||||
{
|
||||
int fd;
|
||||
ssize_t r;
|
||||
off_t off;
|
||||
int to_copy;
|
||||
int config;
|
||||
unsigned char tmp[6];
|
||||
|
||||
r = sysfs_get_active_config(dev, &config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (config == -1)
|
||||
return LIBUSB_ERROR_NOT_FOUND;
|
||||
|
||||
usbi_dbg("active configuration %d", config);
|
||||
|
||||
/* sysfs provides access to an in-memory copy of the device descriptor,
|
||||
* so we use that rather than keeping our own copy */
|
||||
@ -276,6 +364,19 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev,
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
/* device might have been unconfigured since we read bConfigurationValue,
|
||||
* so first check that there is any config descriptor data at all... */
|
||||
off = lseek(fd, 0, SEEK_END);
|
||||
if (off < 1) {
|
||||
usbi_err(DEVICE_CTX(dev), "end seek failed, ret=%d errno=%d",
|
||||
off, errno);
|
||||
close(fd);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (off == DEVICE_DESC_LENGTH) {
|
||||
close(fd);
|
||||
return LIBUSB_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
|
||||
if (off < 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "seek failed, ret=%d errno=%d", off, errno);
|
||||
@ -283,20 +384,54 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev,
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
r = read(fd, buffer, len);
|
||||
close(fd);
|
||||
if (r < 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (r == 0) {
|
||||
usbi_dbg("device is unconfigured");
|
||||
return LIBUSB_ERROR_NOT_FOUND;
|
||||
} else if (r < len) {
|
||||
usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len);
|
||||
return LIBUSB_ERROR_IO;
|
||||
/* unbounded loop: we expect the descriptor to be present under all
|
||||
* circumstances */
|
||||
while (1) {
|
||||
r = read(fd, tmp, sizeof(tmp));
|
||||
if (r < 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d",
|
||||
fd, errno);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (r < sizeof(tmp)) {
|
||||
usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, sizeof(tmp));
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
/* check bConfigurationValue */
|
||||
if (tmp[5] == config)
|
||||
break;
|
||||
|
||||
/* try the next descriptor */
|
||||
off = lseek(fd, 0 - sizeof(tmp), SEEK_CUR);
|
||||
if (off < 0)
|
||||
return LIBUSB_ERROR_IO;
|
||||
|
||||
r = seek_to_next_config(DEVICE_CTX(dev), fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
to_copy = (len < sizeof(tmp)) ? len : sizeof(tmp);
|
||||
memcpy(buffer, tmp, to_copy);
|
||||
if (len > sizeof(tmp)) {
|
||||
r = read(fd, buffer + sizeof(tmp), len - sizeof(tmp));
|
||||
if (r < 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d",
|
||||
fd, errno);
|
||||
r = LIBUSB_ERROR_IO;
|
||||
} else if (r == 0) {
|
||||
usbi_dbg("device is unconfigured");
|
||||
r = LIBUSB_ERROR_NOT_FOUND;
|
||||
} else if (r < len - sizeof(tmp)) {
|
||||
usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len);
|
||||
r = LIBUSB_ERROR_IO;
|
||||
}
|
||||
} else {
|
||||
r = 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int op_get_active_config_descriptor(struct libusb_device *dev,
|
||||
@ -310,14 +445,11 @@ static int op_get_active_config_descriptor(struct libusb_device *dev,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* takes a usbfs fd, attempts to find the requested config and copy a certain
|
||||
* amount of it into an output buffer. a bConfigurationValue of -1 indicates
|
||||
* that the first config should be retreived. */
|
||||
* amount of it into an output buffer. */
|
||||
static int get_config_descriptor(struct libusb_context *ctx, int fd,
|
||||
uint8_t config_index, unsigned char *buffer, size_t len)
|
||||
{
|
||||
unsigned char tmp[8];
|
||||
off_t off;
|
||||
ssize_t r;
|
||||
|
||||
@ -329,28 +461,10 @@ static int get_config_descriptor(struct libusb_context *ctx, int fd,
|
||||
|
||||
/* might need to skip some configuration descriptors to reach the
|
||||
* requested configuration */
|
||||
while (config_index) {
|
||||
struct libusb_config_descriptor config;
|
||||
|
||||
/* read first 8 bytes of descriptor */
|
||||
r = read(fd, tmp, sizeof(tmp));
|
||||
if (r < 0) {
|
||||
usbi_err(ctx, "read failed ret=%d errno=%d", r, errno);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (r < sizeof(tmp)) {
|
||||
usbi_err(ctx, "short descriptor read %d/%d", r, sizeof(tmp));
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
usbi_parse_descriptor(tmp, "bbwbb", &config, 1);
|
||||
|
||||
/* seek forward to end of config */
|
||||
off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR);
|
||||
if (off < 0) {
|
||||
usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
while (config_index > 0) {
|
||||
r = seek_to_next_config(ctx, fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
config_index--;
|
||||
}
|
||||
|
||||
@ -377,6 +491,10 @@ static int op_get_config_descriptor(struct libusb_device *dev,
|
||||
/* always read from usbfs: sysfs only has the active descriptor
|
||||
* this will involve waking the device up, but oh well! */
|
||||
|
||||
/* FIXME: the above is no longer true, new kernels have all descriptors
|
||||
* in the descriptors file. but its kinda hard to detect if the kernel
|
||||
* is sufficiently new. */
|
||||
|
||||
__get_usbfs_path(dev, filename);
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
@ -438,49 +556,6 @@ static int cache_active_config(struct libusb_device *dev, int fd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read the bConfigurationValue for a device */
|
||||
static int sysfs_get_active_config(struct libusb_device *dev, int *config)
|
||||
{
|
||||
char *endptr;
|
||||
char tmp[4] = {0, 0, 0, 0};
|
||||
long num;
|
||||
int fd;
|
||||
size_t r;
|
||||
|
||||
fd = __open_sysfs_attr(dev, "bConfigurationValue");
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = read(fd, tmp, sizeof(tmp));
|
||||
close(fd);
|
||||
if (r < 0) {
|
||||
usbi_err(DEVICE_CTX(dev),
|
||||
"read bConfigurationValue failed ret=%d errno=%d", r, errno);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (r == 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "device unconfigured");
|
||||
*config = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tmp[sizeof(tmp) - 1] != 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "not null-terminated?");
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (tmp[0] == 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "no configuration value?");
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
num = strtol(tmp, &endptr, 10);
|
||||
if (endptr == tmp) {
|
||||
usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
*config = (int) num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send a control message to retrieve active configuration */
|
||||
static int usbfs_get_active_config(struct libusb_device *dev, int fd)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user