diff options
author | Daniel Drake <dsd@gentoo.org> | 2008-05-10 00:12:53 +0100 |
---|---|---|
committer | Daniel Drake <dsd@gentoo.org> | 2008-05-10 00:12:53 +0100 |
commit | d77052c0d630e33737c38d601fd633155f6b2229 (patch) | |
tree | 01a1e14195eb78dda91b3202ed698bd9c32eb300 | |
parent | 74bc842bac1a32a26323da6c3e8af2f66e1b5cfa (diff) | |
download | libusb-d77052c0d630e33737c38d601fd633155f6b2229.tar.gz |
Linux: comprehensive sysfs vs usbfs access
Be more flexible when certain parts of sysfs are not available.
-rw-r--r-- | libusb/os/linux_usbfs.c | 312 |
1 files changed, 210 insertions, 102 deletions
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index aa213e7..6bebc06 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -38,9 +38,6 @@ #include "libusbi.h" #include "linux_usbfs.h" -static const char *usbfs_path = NULL; -static int have_sysfs; - /* sysfs vs usbfs: * opening a usbfs node causes the device to be resumed, so we attempt to * avoid this during enumeration. @@ -50,19 +47,31 @@ static int have_sysfs; * - The binary "descriptors" file was added in 2.6.23. * - The "busnum" file was added in 2.6.22 * - The "devnum" file has been present since pre-2.6.18 - * Hence we check for the existance of a descriptors file to determine whether - * sysfs provides all the information we need. We effectively require 2.6.23 - * in order to avoid waking suspended devices during enumeration. + * - the "bConfigurationValue" file has been present since pre-2.6.18 + * + * If we have bConfigurationValue, busnum, and devnum, then we can determine + * the active configuration without having to open the usbfs node in RDWR mode. + * We assume this is the case if we see the busnum file (indicates 2.6.22+). + * The busnum file is important as that is the only way we can relate sysfs + * devices to usbfs nodes. + * + * If we also have descriptors, we can obtain the device descriptor and active + * configuration without touching usbfs at all. */ +static const char *usbfs_path = NULL; + +/* do we have a busnum to relate devices? this also implies that we can read + * the active configuration through bConfigurationValue */ +static int sysfs_can_relate_devices = -1; + +/* do we have a descriptors file? */ +static int sysfs_has_descriptors = -1; + struct linux_device_priv { - union { - char sysfs_dir[SYSFS_DIR_LENGTH]; - struct { - unsigned char *dev_descriptor; - unsigned char *config_descriptor; - }; - }; + char sysfs_dir[SYSFS_DIR_LENGTH]; + unsigned char *dev_descriptor; + unsigned char *config_descriptor; }; struct linux_device_handle_priv { @@ -165,10 +174,10 @@ static int op_init(void) r = stat(SYSFS_DEVICE_PATH, &statbuf); if (r == 0 && S_ISDIR(statbuf.st_mode)) { usbi_dbg("found usb devices in sysfs"); - have_sysfs = 1; } else { usbi_dbg("sysfs usb info not available"); - have_sysfs = 0; + sysfs_has_descriptors = 0; + sysfs_can_relate_devices = 0; } return 0; @@ -184,17 +193,17 @@ static int usbfs_get_device_descriptor(struct libusb_device *dev, return 0; } -static int open_sysfs_descriptors(struct libusb_device *dev) +static int __open_sysfs_attr(struct libusb_device *dev, const char *attr) { struct linux_device_priv *priv = __device_priv(dev); char filename[PATH_MAX + 1]; int fd; - snprintf(filename, PATH_MAX, "%s/%s/descriptors", SYSFS_DEVICE_PATH, - priv->sysfs_dir); + snprintf(filename, PATH_MAX, "%s/%s/%s", + SYSFS_DEVICE_PATH, priv->sysfs_dir, attr); fd = open(filename, O_RDONLY); if (fd < 0) { - usbi_err("open '%s' failed, ret=%d errno=%d", filename, fd, errno); + usbi_err("open %s failed ret=%d errno=%d", filename, fd, errno); return LIBUSB_ERROR_IO; } @@ -210,7 +219,7 @@ static int sysfs_get_device_descriptor(struct libusb_device *dev, /* sysfs provides access to an in-memory copy of the device descriptor, * so we use that rather than keeping our own copy */ - fd = open_sysfs_descriptors(dev); + fd = __open_sysfs_attr(dev, "descriptors"); if (fd < 0) return fd; @@ -230,7 +239,7 @@ static int sysfs_get_device_descriptor(struct libusb_device *dev, static int op_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer) { - if (have_sysfs) + if (sysfs_has_descriptors) return sysfs_get_device_descriptor(dev, buffer); else return usbfs_get_device_descriptor(dev, buffer); @@ -255,7 +264,7 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev, /* sysfs provides access to an in-memory copy of the device descriptor, * so we use that rather than keeping our own copy */ - fd = open_sysfs_descriptors(dev); + fd = __open_sysfs_attr(dev, "descriptors"); if (fd < 0) return fd; @@ -282,7 +291,7 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev, static int op_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len) { - if (have_sysfs) + if (sysfs_has_descriptors) return sysfs_get_active_config_descriptor(dev, buffer, len); else return usbfs_get_active_config_descriptor(dev, buffer, len); @@ -434,97 +443,161 @@ 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) +{ + 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("read bConfigurationValue failed ret=%d errno=%d", + r, errno); + return LIBUSB_ERROR_IO; + } else if (r == 0) { + usbi_err("short bConfigurationValue read"); + return LIBUSB_ERROR_IO; + } + + if (tmp[sizeof(tmp) - 1] != 0) { + usbi_err("not null-terminated?"); + return LIBUSB_ERROR_IO; + } else if (tmp[0] == 0) { + usbi_err("no configuration value?"); + return LIBUSB_ERROR_IO; + } + + num = strtol(tmp, &endptr, 10); + if (endptr == tmp) { + usbi_err("error converting '%s' to integer", tmp); + return LIBUSB_ERROR_IO; + } + + return (int) num; +} + +/* send a control message to retrieve active configuration */ +static int usbfs_get_active_config(struct libusb_device *dev, int fd) +{ + int active_config; + int r; + + struct usbfs_ctrltransfer ctrl = { + .bmRequestType = LIBUSB_ENDPOINT_IN, + .bRequest = LIBUSB_REQUEST_GET_CONFIGURATION, + .wValue = 0, + .wIndex = 0, + .wLength = 1, + .timeout = 1000, + .data = &active_config + }; + + r = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl); + if (r < 0) { + usbi_err("get_configuration failed ret=%d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } + + return active_config; +} + static int initialize_device(struct libusb_device *dev, uint8_t busnum, uint8_t devaddr, const char *sysfs_dir) { struct linux_device_priv *priv = __device_priv(dev); + unsigned char *dev_buf; char path[PATH_MAX + 1]; + int fd; + int active_config = 0; + ssize_t r; dev->bus_number = busnum; dev->device_address = devaddr; + if (sysfs_dir) + strncpy(priv->sysfs_dir, sysfs_dir, SYSFS_DIR_LENGTH); + + if (sysfs_has_descriptors) + return 0; + + /* cache device descriptor in memory so that we can retrieve it later + * without waking the device up (op_get_device_descriptor) */ + + priv->dev_descriptor = NULL; + priv->config_descriptor = NULL; + + if (sysfs_can_relate_devices) { + active_config = sysfs_get_active_config(dev); + if (active_config < 0) + return active_config; + } + __get_usbfs_path(dev, path); - usbi_dbg("%s", path); - - if (!have_sysfs) { - /* cache device descriptor in memory so that we can retrieve it later - * without waking the device up (op_get_device_descriptor) */ - unsigned char *dev_buf = malloc(DEVICE_DESC_LENGTH); - int fd; - ssize_t r; - int tmp; - int active_config = 0; - - struct usbfs_ctrltransfer ctrl = { - .bmRequestType = LIBUSB_ENDPOINT_IN, - .bRequest = LIBUSB_REQUEST_GET_CONFIGURATION, - .wValue = 0, - .wIndex = 0, - .wLength = 1, - .timeout = 1000, - .data = &active_config - }; - - priv->dev_descriptor = NULL; - priv->config_descriptor = NULL; - if (!dev_buf) - return LIBUSB_ERROR_NO_MEM; + fd = open(path, O_RDWR); + if (fd < 0 && errno == EACCES) { + fd = open(path, O_RDONLY); + /* if we only have read-only access to the device, we cannot + * send a control message to determine the active config. just + * assume the first one is active. */ + active_config = -1; + } - fd = open(path, O_RDWR); - if (fd < 0 && errno == EACCES) { - usbi_dbg("sysfs unavailable and read-only access to usbfs --> " - "cannot determine which configuration is active"); - fd = open(path, O_RDONLY); + if (fd < 0) { + usbi_err("open failed, ret=%d errno=%d", fd, errno); + return LIBUSB_ERROR_IO; + } + + if (!sysfs_can_relate_devices) { + if (active_config == -1) { /* if we only have read-only access to the device, we cannot * send a control message to determine the active config. just * assume the first one is active. */ - active_config = -1; - } - - if (fd < 0) { - usbi_err("open failed, ret=%d errno=%d", fd, errno); - free(dev_buf); - return LIBUSB_ERROR_IO; - } - - r = read(fd, dev_buf, DEVICE_DESC_LENGTH); - if (r < 0) { - usbi_err("read descriptor failed ret=%d errno=%d", fd, errno); - free(dev_buf); - close(fd); - return LIBUSB_ERROR_IO; - } else if (r < DEVICE_DESC_LENGTH) { - usbi_err("short descriptor read (%d)", r); - free(dev_buf); - close(fd); - return LIBUSB_ERROR_IO; - } - - if (active_config == 0) { - /* determine active configuration and cache the descriptor */ - tmp = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl); - if (tmp < 0) { - usbi_err("get_configuration failed ret=%d errno=%d", tmp, errno); - free(dev_buf); + usbi_warn("access to %s is read-only; cannot determine " + "active configuration descriptor", path); + } else { + active_config = usbfs_get_active_config(dev, fd); + if (active_config < 0) { close(fd); - return LIBUSB_ERROR_IO; + return active_config; } } + } - r = cache_active_config(dev, fd, active_config); - if (r < 0) { - free(dev_buf); - close(fd); - return r; - } + dev_buf = malloc(DEVICE_DESC_LENGTH); + if (!dev_buf) { + close(fd); + return LIBUSB_ERROR_NO_MEM; + } - priv->dev_descriptor = dev_buf; + r = read(fd, dev_buf, DEVICE_DESC_LENGTH); + if (r < 0) { + usbi_err("read descriptor failed ret=%d errno=%d", fd, errno); + free(dev_buf); + close(fd); + return LIBUSB_ERROR_IO; + } else if (r < DEVICE_DESC_LENGTH) { + usbi_err("short descriptor read (%d)", r); + free(dev_buf); close(fd); + return LIBUSB_ERROR_IO; } - if (sysfs_dir) - strncpy(priv->sysfs_dir, sysfs_dir, SYSFS_DIR_LENGTH); + r = cache_active_config(dev, fd, active_config); + close(fd); + if (r < 0) { + free(dev_buf); + return r; + } + priv->dev_descriptor = dev_buf; return 0; } @@ -659,9 +732,9 @@ out: } static int sysfs_scan_device(struct discovered_devs **_discdevs, - const char *devname) + const char *devname, int *usbfs_fallback) { - int r = 0; + int r; FILE *fd; char filename[PATH_MAX + 1]; int busnum; @@ -669,9 +742,33 @@ static int sysfs_scan_device(struct discovered_devs **_discdevs, usbi_dbg("scan %s", devname); + /* determine descriptors presence ahead of time, we need to know this + * when we reach initialize_device */ + if (sysfs_has_descriptors == -1) { + struct stat statbuf; + + snprintf(filename, PATH_MAX, "%s/%s/descriptors", SYSFS_DEVICE_PATH, + devname); + r = stat(filename, &statbuf); + if (r == 0 && S_ISREG(statbuf.st_mode)) { + usbi_dbg("sysfs descriptors available"); + sysfs_has_descriptors = 1; + } else { + usbi_dbg("sysfs descriptors not available"); + sysfs_has_descriptors = 0; + } + } + snprintf(filename, PATH_MAX, "%s/%s/busnum", SYSFS_DEVICE_PATH, devname); fd = fopen(filename, "r"); if (!fd) { + if (errno == ENOENT) { + usbi_dbg("busnum not found, cannot relate sysfs to usbfs, " + "falling back on pure usbfs"); + sysfs_can_relate_devices = 0; + *usbfs_fallback = 1; + return LIBUSB_ERROR_OTHER; + } usbi_err("open busnum failed, errno=%d", errno); return LIBUSB_ERROR_IO; } @@ -704,7 +801,8 @@ static int sysfs_scan_device(struct discovered_devs **_discdevs, return enumerate_device(_discdevs, busnum & 0xff, devaddr & 0xff, devname); } -static int sysfs_get_device_list(struct discovered_devs **_discdevs) +static int sysfs_get_device_list(struct discovered_devs **_discdevs, + int *usbfs_fallback) { struct discovered_devs *discdevs = *_discdevs; DIR *devices = opendir(SYSFS_DEVICE_PATH); @@ -723,7 +821,7 @@ static int sysfs_get_device_list(struct discovered_devs **_discdevs) || strchr(entry->d_name, ':')) continue; - r = sysfs_scan_device(&discdevs_new, entry->d_name); + r = sysfs_scan_device(&discdevs_new, entry->d_name, usbfs_fallback); if (r < 0) goto out; discdevs = discdevs_new; @@ -740,11 +838,21 @@ static int op_get_device_list(struct discovered_devs **_discdevs) /* we can retrieve device list and descriptors from sysfs or usbfs. * sysfs is preferable, because if we use usbfs we end up resuming * any autosuspended USB devices. however, sysfs is not available - * everywhere, so we need a usbfs fallback too */ - if (have_sysfs) - return sysfs_get_device_list(_discdevs); - else - return usbfs_get_device_list(_discdevs); + * everywhere, so we need a usbfs fallback too. + * + * as described in the "sysfs vs usbfs" comment, sometimes we have + * sysfs but not enough information to relate sysfs devices to usbfs + * nodes. the usbfs_fallback variable is used to indicate that we should + * fall back on usbfs. + */ + if (sysfs_can_relate_devices != 0) { + int usbfs_fallback = 0; + int r = sysfs_get_device_list(_discdevs, &usbfs_fallback); + if (!usbfs_fallback) + return r; + } + + return usbfs_get_device_list(_discdevs); } static int op_open(struct libusb_device_handle *handle) @@ -791,7 +899,7 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config) return LIBUSB_ERROR_OTHER; } - if (!have_sysfs) { + if (!sysfs_has_descriptors) { /* update our cached active config descriptor */ r = cache_active_config(handle->dev, fd, config); if (r < 0) @@ -929,7 +1037,7 @@ static int op_detach_kernel_driver(struct libusb_device_handle *handle, static void op_destroy_device(struct libusb_device *dev) { struct linux_device_priv *priv = __device_priv(dev); - if (!have_sysfs) { + if (!sysfs_has_descriptors) { if (priv->dev_descriptor) free(priv->dev_descriptor); if (priv->config_descriptor) |